harnessweaver-cli 0.7.1__tar.gz → 0.7.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.
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/CHANGELOG.md +10 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/PKG-INFO +9 -6
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/README.md +8 -5
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/pyproject.toml +1 -1
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/scaffold.py +5 -2
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/README.md +6 -5
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/app/main.py +2 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/README.md +5 -4
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/app/main.py +2 -1
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/README.md +6 -5
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/app/main.py +2 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/README.md +5 -4
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/app/main.py +2 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/README.md +5 -4
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/app/main.py +2 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_init.py +59 -2
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/.gitignore +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/LICENSE +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/__init__.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/_load.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/_yaml.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/add.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/dev.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/doctor.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/evals.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/gateway.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/main.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/plugins.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/policy.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/py.typed +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/replay.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/sidecar.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/spec.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/_dev/compose.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/_dev/otel-collector.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/_plugin/README.md +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/_plugin/pyproject.toml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/_plugin/src/__ns___harness/__init__.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/_plugin/src/__ns___harness/guards.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/_plugin/src/__ns___harness/py.typed +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/_plugin/tests/test_guards.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/app/__init__.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/app/agent.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/app/tools.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/evals/redteam.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/harness.offline.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/harness.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/pyproject.toml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/tests/test_agent.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/tools/lookup_order.tool.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/tools/refund_payment.tool.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/app/__init__.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/app/chat.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/evals/golden.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/harness.offline.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/harness.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/pyproject.toml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/tests/test_chat.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/app/__init__.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/app/models.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/evals/golden.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/harness.offline.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/harness.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/prompts/extract@1.md +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/pyproject.toml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/extraction/tests/test_extract.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/app/__init__.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/app/flow.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/data/kb.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/evals/golden.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/harness.offline.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/harness.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/prompts/answer@1.md +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/pyproject.toml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/tests/test_answer.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/app/__init__.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/app/steps.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/evals/golden.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/harness.offline.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/harness.yaml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/prompts/classify@1.md +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/prompts/reply@1.md +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/prompts/summarize@1.md +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/pyproject.toml +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/workflow/tests/test_pipeline.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/tools.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/conftest.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_add_and_tools.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_cli.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_dev.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_doctor.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_evals.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_gateway.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_plugins.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_policy_and_replay.py +0 -0
- {harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/tests/test_spec.py +0 -0
|
@@ -4,6 +4,16 @@ All notable changes to the `harnessweaver` command and its project templates. Th
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## 0.7.2 - 2026-11-08
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- `harnessweaver init` ends with next steps that work right after `uvx --from harnessweaver-cli harnessweaver init`, with nothing installed yet: `uv sync`, then `uv run harnessweaver spec validate --env offline` and `uv run harnessweaver test`. The templates' READMEs run their commands the same way, and the package's README uses absolute links, which work on PyPI.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- A template's `app/main.py` now prints why a run didn't complete, as `error: <code>: <message>` (for example `error: provider_error: openai.chat needs $LITELLM_API_KEY`), and the `chat` template's terminal app shows the reason next to the status. A first run against a real model with no key or no gateway printed only `status: failed` and `output: None`.
|
|
16
|
+
|
|
7
17
|
## 0.7.1 - 2026-11-01
|
|
8
18
|
|
|
9
19
|
The first version published to PyPI.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: harnessweaver-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Developer tooling for HarnessWeaver: the harnessweaver command.
|
|
5
5
|
Author-email: Muthu Kumaran R <rsmk7new@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -23,22 +23,25 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
|
|
24
24
|
# harnessweaver-cli
|
|
25
25
|
|
|
26
|
-
Developer tooling for [HarnessWeaver](
|
|
26
|
+
Developer tooling for [HarnessWeaver](https://github.com/Wise-DreamerZ/HarnessWeaver). Installs the `harnessweaver` command. The full reference, with every option and exit code, is [the CLI page](https://harnessweaver.wisedreamerz.com/docs/cli/) of the docs.
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
uv add --dev harnessweaver-cli
|
|
30
|
-
harnessweaver --help
|
|
30
|
+
uv run harnessweaver --help
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
## Start a project
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
harnessweaver init invoice-bot
|
|
36
|
+
uvx --from harnessweaver-cli harnessweaver init invoice-bot # the extraction template; -t picks another
|
|
37
37
|
cd invoice-bot
|
|
38
|
-
|
|
39
|
-
harnessweaver
|
|
38
|
+
uv sync # the project's own dependencies, the CLI among them
|
|
39
|
+
uv run harnessweaver spec validate --env offline # load it the way the app will
|
|
40
|
+
uv run harnessweaver test # its tests, offline
|
|
40
41
|
```
|
|
41
42
|
|
|
43
|
+
`uvx` runs the command once without installing it. A new project lists `harnessweaver-cli` among its development dependencies, so from then on `uv run harnessweaver` is the project's own copy.
|
|
44
|
+
|
|
42
45
|
`init` copies a template and puts the project name in it; there is no template
|
|
43
46
|
language. A plugin's templates are named `<namespace>.<template>`
|
|
44
47
|
(`harnessweaver init -t acme.rag`). Every template's output passes its own tests
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
# harnessweaver-cli
|
|
2
2
|
|
|
3
|
-
Developer tooling for [HarnessWeaver](
|
|
3
|
+
Developer tooling for [HarnessWeaver](https://github.com/Wise-DreamerZ/HarnessWeaver). Installs the `harnessweaver` command. The full reference, with every option and exit code, is [the CLI page](https://harnessweaver.wisedreamerz.com/docs/cli/) of the docs.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
uv add --dev harnessweaver-cli
|
|
7
|
-
harnessweaver --help
|
|
7
|
+
uv run harnessweaver --help
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
## Start a project
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
harnessweaver init invoice-bot
|
|
13
|
+
uvx --from harnessweaver-cli harnessweaver init invoice-bot # the extraction template; -t picks another
|
|
14
14
|
cd invoice-bot
|
|
15
|
-
|
|
16
|
-
harnessweaver
|
|
15
|
+
uv sync # the project's own dependencies, the CLI among them
|
|
16
|
+
uv run harnessweaver spec validate --env offline # load it the way the app will
|
|
17
|
+
uv run harnessweaver test # its tests, offline
|
|
17
18
|
```
|
|
18
19
|
|
|
20
|
+
`uvx` runs the command once without installing it. A new project lists `harnessweaver-cli` among its development dependencies, so from then on `uv run harnessweaver` is the project's own copy.
|
|
21
|
+
|
|
19
22
|
`init` copies a template and puts the project name in it; there is no template
|
|
20
23
|
language. A plugin's templates are named `<namespace>.<template>`
|
|
21
24
|
(`harnessweaver init -t acme.rag`). Every template's output passes its own tests
|
|
@@ -137,10 +137,13 @@ def init(
|
|
|
137
137
|
raise fail(f"invalid project name {app_name!r}: use lowercase letters, digits and dashes")
|
|
138
138
|
written = render_template(template, directory, app_name, force=force)
|
|
139
139
|
typer.echo(f"created {len(written)} files in {directory} from the {template} template")
|
|
140
|
+
# The project installs its own CLI, so these steps work right after
|
|
141
|
+
# `uvx --from harnessweaver-cli harnessweaver init`, with nothing on PATH yet.
|
|
140
142
|
typer.echo("next:")
|
|
141
143
|
typer.echo(f" cd {directory}")
|
|
142
|
-
typer.echo("
|
|
143
|
-
typer.echo(" harnessweaver
|
|
144
|
+
typer.echo(" uv sync")
|
|
145
|
+
typer.echo(" uv run harnessweaver spec validate --env offline")
|
|
146
|
+
typer.echo(" uv run harnessweaver test")
|
|
144
147
|
|
|
145
148
|
|
|
146
149
|
def run_tests(
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/agent/README.md
RENAMED
|
@@ -8,11 +8,12 @@ A support agent that looks up orders and issues refunds. A Pydantic AI agent run
|
|
|
8
8
|
- `evals/redteam.yaml` is the red-team set: injected instructions and over-the-limit refunds are blocked, and a large refund waits for a person.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
|
|
12
|
-
harnessweaver
|
|
13
|
-
|
|
14
|
-
harnessweaver
|
|
15
|
-
|
|
11
|
+
uv sync # install the project and its tools
|
|
12
|
+
uv run harnessweaver spec validate --env offline # check the spec
|
|
13
|
+
uv run harnessweaver test # run the tests offline
|
|
14
|
+
uv run harnessweaver eval run --env offline # grade the red-team set
|
|
15
|
+
uv run harnessweaver policy test # once you write policy_tests.yaml
|
|
16
|
+
HARNESSWEAVER_ENV=offline uv run python -m app.main # ask the agent once
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
Approve a waiting refund with `harness.approve(run_id, approval_id, approver=...)`; the agent continues from where it stopped, and only the approved call runs. After changing a tool's description or schema, review it and run `harnessweaver tools pin`.
|
|
@@ -13,6 +13,8 @@ async def main() -> None:
|
|
|
13
13
|
result = await harness.run("support", "Where is my order A-1001?", principal="demo")
|
|
14
14
|
print(f"status: {result.status.value}")
|
|
15
15
|
print(f"output: {result.output}")
|
|
16
|
+
if result.error is not None: # why the run didn't complete
|
|
17
|
+
print(f"error: {result.error.code.value}: {result.error.message}")
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
if __name__ == "__main__":
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/README.md
RENAMED
|
@@ -8,8 +8,9 @@ A chat assistant whose answers stream to the user, governed by HarnessWeaver.
|
|
|
8
8
|
- `harness.offline.yaml` swaps the model for a scripted one that streams, so everything runs with no network.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
|
|
12
|
-
harnessweaver
|
|
13
|
-
harnessweaver
|
|
14
|
-
|
|
11
|
+
uv sync # install the project and its tools
|
|
12
|
+
uv run harnessweaver spec validate --env offline # check the spec
|
|
13
|
+
uv run harnessweaver test # run the tests offline
|
|
14
|
+
uv run harnessweaver eval run --env offline # grade evals/golden.yaml
|
|
15
|
+
HARNESSWEAVER_ENV=offline uv run python -m app.main # chat in the terminal
|
|
15
16
|
```
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/app/main.py
RENAMED
|
@@ -26,7 +26,8 @@ async def main() -> None:
|
|
|
26
26
|
if event.type == "chunk" and event.chunk is not None:
|
|
27
27
|
print(event.chunk.delta, end="", flush=True)
|
|
28
28
|
result = await harness.result(run_id)
|
|
29
|
-
|
|
29
|
+
why = f": {result.error.message}" if result.error is not None else ""
|
|
30
|
+
print("" if result.ok else f"[{result.status.value}{why}]")
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
if __name__ == "__main__":
|
|
@@ -6,11 +6,12 @@ Typed extraction from customer support messages, governed by HarnessWeaver.
|
|
|
6
6
|
- `harness.offline.yaml` swaps the model for a scripted one, so everything runs with no network.
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
|
|
10
|
-
harnessweaver
|
|
11
|
-
harnessweaver
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
uv sync # install the project and its tools
|
|
10
|
+
uv run harnessweaver spec validate --env offline # check the spec
|
|
11
|
+
uv run harnessweaver doctor --env offline # adoption level and what to do next
|
|
12
|
+
uv run harnessweaver test # run the tests offline
|
|
13
|
+
uv run harnessweaver eval run --env offline # grade evals/golden.yaml; exits 1 on a regression
|
|
14
|
+
HARNESSWEAVER_ENV=offline uv run python -m app.main # run the task once
|
|
14
15
|
```
|
|
15
16
|
|
|
16
17
|
To call a real model, run LiteLLM Proxy at `base_url`, set `LITELLM_API_KEY`, and map the `fast` alias to a model in `harness.yaml`.
|
|
@@ -15,6 +15,8 @@ async def main() -> None:
|
|
|
15
15
|
result = await harness.run("extract", Message(text=SAMPLE), principal="demo")
|
|
16
16
|
print(f"status: {result.status.value}")
|
|
17
17
|
print(f"output: {result.output}")
|
|
18
|
+
if result.error is not None: # why the run didn't complete
|
|
19
|
+
print(f"error: {result.error.code.value}: {result.error.message}")
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
if __name__ == "__main__":
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/README.md
RENAMED
|
@@ -8,10 +8,11 @@ Answers from a knowledge base, with sources, governed by HarnessWeaver.
|
|
|
8
8
|
- `harness.offline.yaml` swaps the model for a scripted one, so everything runs with no network.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
|
|
12
|
-
harnessweaver
|
|
13
|
-
harnessweaver
|
|
14
|
-
|
|
11
|
+
uv sync # install the project and its tools
|
|
12
|
+
uv run harnessweaver spec validate --env offline # check the spec
|
|
13
|
+
uv run harnessweaver test # run the tests offline
|
|
14
|
+
uv run harnessweaver eval run --env offline # grade evals/golden.yaml
|
|
15
|
+
HARNESSWEAVER_ENV=offline uv run python -m app.main # answer one question
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
Add passages to `data/kb.yaml`, or replace the `memory` provider with a vector store plugin; the ACL re-check and the citation guard stay the same.
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/app/main.py
RENAMED
|
@@ -13,6 +13,8 @@ async def main() -> None:
|
|
|
13
13
|
result = await harness.run("answer", "How long does a refund take?", principal="demo")
|
|
14
14
|
print(f"status: {result.status.value}")
|
|
15
15
|
print(f"output: {result.output}")
|
|
16
|
+
if result.error is not None: # why the run didn't complete
|
|
17
|
+
print(f"error: {result.error.code.value}: {result.error.message}")
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
if __name__ == "__main__":
|
|
@@ -7,10 +7,11 @@ A support-ticket pipeline: clean the ticket, then classify and summarize it in p
|
|
|
7
7
|
- `harness.offline.yaml` swaps the model for a scripted one whose first draft misses the category, so the review loop shows offline.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
11
|
-
harnessweaver
|
|
12
|
-
harnessweaver
|
|
13
|
-
|
|
10
|
+
uv sync # install the project and its tools
|
|
11
|
+
uv run harnessweaver spec validate --env offline # check the spec
|
|
12
|
+
uv run harnessweaver test # run the tests offline
|
|
13
|
+
uv run harnessweaver eval run --env offline # grade evals/golden.yaml
|
|
14
|
+
HARNESSWEAVER_ENV=offline uv run python -m app.main # triage one ticket
|
|
14
15
|
```
|
|
15
16
|
|
|
16
17
|
Finished steps are checkpointed: after an approval, only the interrupted step runs again.
|
|
@@ -14,6 +14,8 @@ async def main() -> None:
|
|
|
14
14
|
result = await harness.run("triage", ticket, principal="demo")
|
|
15
15
|
print(f"status: {result.status.value}")
|
|
16
16
|
print(f"output: {result.output}")
|
|
17
|
+
if result.error is not None: # why the run didn't complete
|
|
18
|
+
print(f"error: {result.error.code.value}: {result.error.message}")
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
if __name__ == "__main__":
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""``harnessweaver init`` and ``harnessweaver test``, including T6: a template's own tests pass."""
|
|
2
2
|
|
|
3
|
+
import importlib
|
|
3
4
|
import sys
|
|
4
5
|
from pathlib import Path
|
|
6
|
+
from types import ModuleType
|
|
5
7
|
|
|
6
8
|
import pytest
|
|
7
9
|
from typer.testing import CliRunner
|
|
@@ -9,7 +11,7 @@ from typer.testing import CliRunner
|
|
|
9
11
|
import harnessweaver as hw
|
|
10
12
|
from harnessweaver.core.registry import PluginCatalog, PluginSource
|
|
11
13
|
from harnessweaver_cli.main import app
|
|
12
|
-
from harnessweaver_cli.scaffold import BUILTIN_TEMPLATES
|
|
14
|
+
from harnessweaver_cli.scaffold import BUILTIN_TEMPLATES, render_template
|
|
13
15
|
|
|
14
16
|
runner = CliRunner()
|
|
15
17
|
|
|
@@ -40,7 +42,62 @@ def test_init_renders_the_extraction_template(tmp_path: Path) -> None:
|
|
|
40
42
|
assert "__app_name__" not in "".join(
|
|
41
43
|
(target / f).read_text(encoding="utf-8") for f in files(target)
|
|
42
44
|
)
|
|
43
|
-
|
|
45
|
+
# The next steps work right after `uvx ... init`, with nothing installed yet.
|
|
46
|
+
assert " uv sync\n" in result.output
|
|
47
|
+
assert " uv run harnessweaver spec validate --env offline\n" in result.output
|
|
48
|
+
assert " uv run harnessweaver test\n" in result.output
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
BROKEN = "models:\n provider: scripted\n replies: [{ fail: true }]\n"
|
|
52
|
+
"""An overlay whose model fails every call, the way a missing key or gateway does."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def forget_app() -> None:
|
|
56
|
+
"""Drop the ``app`` modules, so the next import reads the current template's code."""
|
|
57
|
+
for name in [m for m in sys.modules if m == "app" or m.startswith("app.")]:
|
|
58
|
+
del sys.modules[name]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def broken_app(template: str, root: Path, monkeypatch: pytest.MonkeyPatch) -> ModuleType:
|
|
62
|
+
"""Render ``template`` into ``root`` with a failing model, and import its ``app.main``."""
|
|
63
|
+
render_template(template, root, "demo")
|
|
64
|
+
(root / "harness.broken.yaml").write_text(BROKEN, encoding="utf-8")
|
|
65
|
+
monkeypatch.setenv("HARNESSWEAVER_ENV", "broken")
|
|
66
|
+
monkeypatch.syspath_prepend(str(root))
|
|
67
|
+
forget_app()
|
|
68
|
+
return importlib.import_module("app.main")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.anyio
|
|
72
|
+
@pytest.mark.parametrize("template", ["extraction", "rag", "agent", "workflow"])
|
|
73
|
+
async def test_a_template_app_says_why_its_run_failed(
|
|
74
|
+
template: str,
|
|
75
|
+
tmp_path: Path,
|
|
76
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
77
|
+
capsys: pytest.CaptureFixture[str],
|
|
78
|
+
) -> None:
|
|
79
|
+
"""A first run against a real model often fails (no key, no gateway): say why."""
|
|
80
|
+
try:
|
|
81
|
+
await broken_app(template, tmp_path / "demo", monkeypatch).main()
|
|
82
|
+
finally:
|
|
83
|
+
forget_app()
|
|
84
|
+
out = capsys.readouterr().out
|
|
85
|
+
assert "status: failed\n" in out
|
|
86
|
+
assert "error: provider_error: the script says this call fails\n" in out
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@pytest.mark.anyio
|
|
90
|
+
async def test_the_chat_template_says_why_a_turn_failed(
|
|
91
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
92
|
+
) -> None:
|
|
93
|
+
try:
|
|
94
|
+
main = broken_app("chat", tmp_path / "demo", monkeypatch)
|
|
95
|
+
messages = iter(["hello", ""])
|
|
96
|
+
monkeypatch.setattr(main, "ask", lambda: next(messages))
|
|
97
|
+
await main.main()
|
|
98
|
+
finally:
|
|
99
|
+
forget_app()
|
|
100
|
+
assert "[failed: the script says this call fails]" in capsys.readouterr().out
|
|
44
101
|
|
|
45
102
|
|
|
46
103
|
def test_init_never_overwrites_without_force(tmp_path: Path) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/chat/app/chat.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/app/flow.py
RENAMED
|
File without changes
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/data/kb.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{harnessweaver_cli-0.7.1 → harnessweaver_cli-0.7.2}/src/harnessweaver_cli/templates/rag/harness.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|