agento11y-harbor 0.20.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.
- agento11y_harbor-0.20.0/LICENSE +3 -0
- agento11y_harbor-0.20.0/PKG-INFO +213 -0
- agento11y_harbor-0.20.0/README.md +197 -0
- agento11y_harbor-0.20.0/agento11y_harbor/__init__.py +6 -0
- agento11y_harbor-0.20.0/agento11y_harbor/cli.py +114 -0
- agento11y_harbor-0.20.0/agento11y_harbor/integration.py +864 -0
- agento11y_harbor-0.20.0/agento11y_harbor/plugin.py +353 -0
- agento11y_harbor-0.20.0/agento11y_harbor/py.typed +1 -0
- agento11y_harbor-0.20.0/agento11y_harbor/runner.py +108 -0
- agento11y_harbor-0.20.0/agento11y_harbor.egg-info/PKG-INFO +213 -0
- agento11y_harbor-0.20.0/agento11y_harbor.egg-info/SOURCES.txt +21 -0
- agento11y_harbor-0.20.0/agento11y_harbor.egg-info/dependency_links.txt +1 -0
- agento11y_harbor-0.20.0/agento11y_harbor.egg-info/entry_points.txt +5 -0
- agento11y_harbor-0.20.0/agento11y_harbor.egg-info/requires.txt +4 -0
- agento11y_harbor-0.20.0/agento11y_harbor.egg-info/top_level.txt +1 -0
- agento11y_harbor-0.20.0/pyproject.toml +28 -0
- agento11y_harbor-0.20.0/setup.cfg +4 -0
- agento11y_harbor-0.20.0/tests/test_cli.py +109 -0
- agento11y_harbor-0.20.0/tests/test_contract.py +32 -0
- agento11y_harbor-0.20.0/tests/test_error_isolation.py +272 -0
- agento11y_harbor-0.20.0/tests/test_integration.py +487 -0
- agento11y_harbor-0.20.0/tests/test_plugin.py +546 -0
- agento11y_harbor-0.20.0/tests/test_runner_identity.py +52 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agento11y-harbor
|
|
3
|
+
Version: 0.20.0
|
|
4
|
+
Summary: Harbor live evaluation and job import for the Grafana Agent Observability Python SDK
|
|
5
|
+
License: SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
|
|
7
|
+
See /LICENSE at repository root for full license text.
|
|
8
|
+
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: agento11y>=0.21.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8.2.0; extra == "dev"
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# Harbor integration
|
|
18
|
+
|
|
19
|
+
`agento11y-harbor` publishes [Harbor](https://github.com/harbor-framework/harbor)
|
|
20
|
+
evaluations to Agent Observability in Grafana Cloud. You can publish a job
|
|
21
|
+
while it runs, import a job that already finished, or run explicit Harbor
|
|
22
|
+
trials from Python.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
Install the package in the same Python environment as Harbor, so Harbor can
|
|
27
|
+
discover the `agento11y` job plugin. With a `uv` tool install:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv tool install harbor --with-executables-from agento11y-harbor
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or with `pip`, in Harbor's environment:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install agento11y-harbor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The package needs `agento11y` 0.20.0 or later and a Harbor release that
|
|
40
|
+
supports job plugins (`harbor run --plugin`).
|
|
41
|
+
|
|
42
|
+
## Configure Grafana Cloud
|
|
43
|
+
|
|
44
|
+
Set these environment variables before you run or import a job:
|
|
45
|
+
|
|
46
|
+
- `AGENTO11Y_ENDPOINT`: your Grafana Cloud Agent Observability endpoint.
|
|
47
|
+
- `AGENTO11Y_AUTH_TOKEN` and, when your stack requires it,
|
|
48
|
+
`AGENTO11Y_AUTH_TENANT_ID`: your Grafana Cloud ingestion credentials.
|
|
49
|
+
- `AGENTO11Y_GRAFANA_URL`: your Grafana Cloud stack URL, for experiment links.
|
|
50
|
+
- `AGENTO11Y_SERVICE_ACCOUNT_TOKEN`: a Grafana service account token. You need
|
|
51
|
+
it only to publish a suite checkpoint with `--publish-suite`.
|
|
52
|
+
|
|
53
|
+
Refer to the [Grafana Cloud setup guide](https://grafana.com/docs/grafana-cloud/machine-learning/agent-observability/get-started/grafana-cloud/)
|
|
54
|
+
for the credentials. `agento11y harbor` also loads the credentials that
|
|
55
|
+
`agento11y login` saved.
|
|
56
|
+
|
|
57
|
+
## Publish a job while it runs
|
|
58
|
+
|
|
59
|
+
Run Harbor through the `agento11y` CLI. Every argument after `--` goes to
|
|
60
|
+
`harbor run` unchanged:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
agento11y harbor -- \
|
|
64
|
+
--dataset terminal-bench@2.0 \
|
|
65
|
+
--agent codex \
|
|
66
|
+
--model openai/gpt-5
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`agento11y harbor run -- ...` is the same command. The launcher adds
|
|
70
|
+
`--plugin agento11y`, so `harbor run --plugin agento11y ...` works too. Pass
|
|
71
|
+
plugin options with `--plugin-kwarg`, for example
|
|
72
|
+
`--plugin-kwarg pass_threshold=0.5`. The options are `name`, `experiment_id`,
|
|
73
|
+
`main_reward` (default `reward`), `pass_threshold` (default `1.0`), and
|
|
74
|
+
`upload_artifacts` (default `true`).
|
|
75
|
+
|
|
76
|
+
The plugin creates one experiment per agent, model, and dataset. Each trial
|
|
77
|
+
appears when Harbor starts its agent, after the environment and agent setup,
|
|
78
|
+
so the experiment records the agent version Harbor detects. A trial whose
|
|
79
|
+
setup fails appears when it ends. When the trial ends, the plugin attaches its
|
|
80
|
+
rewards, duration, tokens, cost, errors, and verifier artifacts. The main
|
|
81
|
+
reward is the pass/fail verdict, and other rewards are diagnostics.
|
|
82
|
+
|
|
83
|
+
If your agent publishes its own conversation, put its ID in
|
|
84
|
+
`AgentContext.metadata["agento11y.conversation_id"]`, and the trial links to
|
|
85
|
+
it. Otherwise, the plugin imports the trial's ATIF trajectory as a
|
|
86
|
+
conversation. ATIF import creates generations, not traces. Each tool call's
|
|
87
|
+
arguments keep their first 4 KiB, and each step's prompt and response keep
|
|
88
|
+
their first 1 MiB, so one large tool result can't fail the import.
|
|
89
|
+
|
|
90
|
+
## Import a completed job
|
|
91
|
+
|
|
92
|
+
Publish a Harbor job directory that already finished. Harbor writes jobs to
|
|
93
|
+
`jobs/` in the directory you ran it from, unless you pass `--jobs-dir`:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
agento11y harbor export jobs/<job-name>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The importer reads Harbor's `result.json`, ATIF, reward, and verifier files.
|
|
100
|
+
It doesn't need Harbor installed. Run `agento11y harbor export --help` for the
|
|
101
|
+
flags:
|
|
102
|
+
|
|
103
|
+
- `--experiment-id`: the base experiment ID. The default is the Harbor job ID.
|
|
104
|
+
- `--suite-id`: the suite ID for the imported cases. The default is derived
|
|
105
|
+
from the dataset. On its own, it doesn't publish anything.
|
|
106
|
+
- `--publish-suite subset|replace`: store the job's tasks as a published suite
|
|
107
|
+
checkpoint before the import. `subset` adds and updates cases. `replace` also
|
|
108
|
+
removes stored cases that aren't in the job.
|
|
109
|
+
- `--main-reward`, `--pass-threshold`, and `--no-artifacts`: the same options
|
|
110
|
+
as the plugin.
|
|
111
|
+
|
|
112
|
+
From Python, call `publish_harbor_job`:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from agento11y.experiments import TestSuitesClient
|
|
116
|
+
from agento11y_harbor import publish_harbor_job
|
|
117
|
+
|
|
118
|
+
published = publish_harbor_job(
|
|
119
|
+
"jobs/my-job",
|
|
120
|
+
test_suites_client=TestSuitesClient(),
|
|
121
|
+
suite_publication_policy="subset",
|
|
122
|
+
)
|
|
123
|
+
for run in published.runs:
|
|
124
|
+
print(run.url)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Experiment IDs
|
|
128
|
+
|
|
129
|
+
The plugin and the importer use the same experiment ID:
|
|
130
|
+
`<experiment_id or Harbor job ID>-<candidate hash>`. The hash comes from the
|
|
131
|
+
agent name, model, and dataset, so it's the same for every run of the same
|
|
132
|
+
candidate.
|
|
133
|
+
|
|
134
|
+
A finalized experiment can't change. When you import a job, a candidate whose
|
|
135
|
+
experiment is already finalized, either live or by an earlier import, is
|
|
136
|
+
skipped with a warning, and the import fails if every candidate is. Pass a new
|
|
137
|
+
`--experiment-id` to publish the job as separate experiments. Resuming a
|
|
138
|
+
Harbor job also reuses the job ID, so pass a new `experiment_id` plugin option
|
|
139
|
+
when the job's experiments already finalized.
|
|
140
|
+
|
|
141
|
+
## Errors and retries
|
|
142
|
+
|
|
143
|
+
Publishing problems never stop the Harbor job. When a trial can't be
|
|
144
|
+
published, or one of its files can't be read, that trial is marked errored and
|
|
145
|
+
the rest of the job continues. The plugin logs a warning. The errored trial's
|
|
146
|
+
rewards stay diagnostic.
|
|
147
|
+
|
|
148
|
+
An agent timeout or non-zero exit doesn't error a trial when Harbor's verifier
|
|
149
|
+
still produced the main reward: like Harbor's own metrics, the reward decides
|
|
150
|
+
pass or fail, and the agent error is kept in the score metadata. Any other
|
|
151
|
+
Harbor exception, or a missing main reward, errors the trial.
|
|
152
|
+
|
|
153
|
+
Any errored trial finalizes its experiment as failed, and the experiment error
|
|
154
|
+
lists the errored trials. An importer skips a trial whose `result.json` it
|
|
155
|
+
can't read and logs a warning. Each candidate is imported on its own, so one
|
|
156
|
+
that fails to publish doesn't stop the others. The export command lists it and
|
|
157
|
+
exits with status 1, and rerunning the command publishes only what's missing.
|
|
158
|
+
|
|
159
|
+
If the plugin can't create an experiment, the candidate's next trial tries
|
|
160
|
+
again. Once it exists, the experiment is finalized as failed, because it's
|
|
161
|
+
missing the trials that started before then. If it never exists, the plugin logs
|
|
162
|
+
how many trials it left out when the job ends. Either way, export the job
|
|
163
|
+
directory to publish every trial, with a new `--experiment-id` if the
|
|
164
|
+
experiment exists.
|
|
165
|
+
|
|
166
|
+
If the Harbor process is interrupted, for example with Ctrl-C, the plugin marks
|
|
167
|
+
trials without a result errored and finalizes the experiments as failed. To
|
|
168
|
+
publish the finished trials, export the job directory with a new
|
|
169
|
+
`--experiment-id`.
|
|
170
|
+
|
|
171
|
+
Harbor retries a failed trial under the same trial name. The plugin keeps one
|
|
172
|
+
trial per name and publishes only the final attempt, so a failure that passes
|
|
173
|
+
on retry counts once, as a pass. The importer sees the same result, because
|
|
174
|
+
Harbor deletes the files of superseded attempts. Separate attempts from
|
|
175
|
+
`--n-attempts` are separate trials. Their attempt numbers follow the order
|
|
176
|
+
their agents start live and trial-name order on import, so compare them by experiment, not by
|
|
177
|
+
attempt number.
|
|
178
|
+
|
|
179
|
+
## Run trials from Python
|
|
180
|
+
|
|
181
|
+
`run_harbor` runs explicit Harbor `TrialConfig` objects for one agent and
|
|
182
|
+
model, one case at a time. It needs Harbor installed:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from agento11y_harbor import run_harbor
|
|
186
|
+
|
|
187
|
+
run = await run_harbor(suite, configs, name="Harbor regression", agent_env=otel_env)
|
|
188
|
+
print(run.url)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`configs` maps each suite case ID to a `harbor.models.trial.config.TrialConfig`.
|
|
192
|
+
The runner adds `agent_env`, `TRACEPARENT`, `TRACESTATE`, and the
|
|
193
|
+
`AGENTO11Y_*` trial identity to each agent's environment. A Python agent can
|
|
194
|
+
continue the trace and read the trial with `TrialRef.from_env()`:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
import os
|
|
198
|
+
from opentelemetry import context, propagate
|
|
199
|
+
|
|
200
|
+
carrier = {key.lower(): os.environ[key] for key in ("TRACEPARENT", "TRACESTATE") if key in os.environ}
|
|
201
|
+
token = context.attach(propagate.extract(carrier))
|
|
202
|
+
try:
|
|
203
|
+
run_instrumented_agent()
|
|
204
|
+
finally:
|
|
205
|
+
context.detach(token)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The live plugin can't add environment variables to agents, because Harbor
|
|
209
|
+
creates each agent before the plugin's hooks run. With the plugin, link
|
|
210
|
+
conversations through `AgentContext.metadata` instead.
|
|
211
|
+
|
|
212
|
+
`run_harbor` doesn't retry trials or run them concurrently. Use
|
|
213
|
+
`agento11y harbor` for Harbor's scheduler, retries, and datasets.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Harbor integration
|
|
2
|
+
|
|
3
|
+
`agento11y-harbor` publishes [Harbor](https://github.com/harbor-framework/harbor)
|
|
4
|
+
evaluations to Agent Observability in Grafana Cloud. You can publish a job
|
|
5
|
+
while it runs, import a job that already finished, or run explicit Harbor
|
|
6
|
+
trials from Python.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
Install the package in the same Python environment as Harbor, so Harbor can
|
|
11
|
+
discover the `agento11y` job plugin. With a `uv` tool install:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv tool install harbor --with-executables-from agento11y-harbor
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or with `pip`, in Harbor's environment:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install agento11y-harbor
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The package needs `agento11y` 0.20.0 or later and a Harbor release that
|
|
24
|
+
supports job plugins (`harbor run --plugin`).
|
|
25
|
+
|
|
26
|
+
## Configure Grafana Cloud
|
|
27
|
+
|
|
28
|
+
Set these environment variables before you run or import a job:
|
|
29
|
+
|
|
30
|
+
- `AGENTO11Y_ENDPOINT`: your Grafana Cloud Agent Observability endpoint.
|
|
31
|
+
- `AGENTO11Y_AUTH_TOKEN` and, when your stack requires it,
|
|
32
|
+
`AGENTO11Y_AUTH_TENANT_ID`: your Grafana Cloud ingestion credentials.
|
|
33
|
+
- `AGENTO11Y_GRAFANA_URL`: your Grafana Cloud stack URL, for experiment links.
|
|
34
|
+
- `AGENTO11Y_SERVICE_ACCOUNT_TOKEN`: a Grafana service account token. You need
|
|
35
|
+
it only to publish a suite checkpoint with `--publish-suite`.
|
|
36
|
+
|
|
37
|
+
Refer to the [Grafana Cloud setup guide](https://grafana.com/docs/grafana-cloud/machine-learning/agent-observability/get-started/grafana-cloud/)
|
|
38
|
+
for the credentials. `agento11y harbor` also loads the credentials that
|
|
39
|
+
`agento11y login` saved.
|
|
40
|
+
|
|
41
|
+
## Publish a job while it runs
|
|
42
|
+
|
|
43
|
+
Run Harbor through the `agento11y` CLI. Every argument after `--` goes to
|
|
44
|
+
`harbor run` unchanged:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
agento11y harbor -- \
|
|
48
|
+
--dataset terminal-bench@2.0 \
|
|
49
|
+
--agent codex \
|
|
50
|
+
--model openai/gpt-5
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`agento11y harbor run -- ...` is the same command. The launcher adds
|
|
54
|
+
`--plugin agento11y`, so `harbor run --plugin agento11y ...` works too. Pass
|
|
55
|
+
plugin options with `--plugin-kwarg`, for example
|
|
56
|
+
`--plugin-kwarg pass_threshold=0.5`. The options are `name`, `experiment_id`,
|
|
57
|
+
`main_reward` (default `reward`), `pass_threshold` (default `1.0`), and
|
|
58
|
+
`upload_artifacts` (default `true`).
|
|
59
|
+
|
|
60
|
+
The plugin creates one experiment per agent, model, and dataset. Each trial
|
|
61
|
+
appears when Harbor starts its agent, after the environment and agent setup,
|
|
62
|
+
so the experiment records the agent version Harbor detects. A trial whose
|
|
63
|
+
setup fails appears when it ends. When the trial ends, the plugin attaches its
|
|
64
|
+
rewards, duration, tokens, cost, errors, and verifier artifacts. The main
|
|
65
|
+
reward is the pass/fail verdict, and other rewards are diagnostics.
|
|
66
|
+
|
|
67
|
+
If your agent publishes its own conversation, put its ID in
|
|
68
|
+
`AgentContext.metadata["agento11y.conversation_id"]`, and the trial links to
|
|
69
|
+
it. Otherwise, the plugin imports the trial's ATIF trajectory as a
|
|
70
|
+
conversation. ATIF import creates generations, not traces. Each tool call's
|
|
71
|
+
arguments keep their first 4 KiB, and each step's prompt and response keep
|
|
72
|
+
their first 1 MiB, so one large tool result can't fail the import.
|
|
73
|
+
|
|
74
|
+
## Import a completed job
|
|
75
|
+
|
|
76
|
+
Publish a Harbor job directory that already finished. Harbor writes jobs to
|
|
77
|
+
`jobs/` in the directory you ran it from, unless you pass `--jobs-dir`:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
agento11y harbor export jobs/<job-name>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The importer reads Harbor's `result.json`, ATIF, reward, and verifier files.
|
|
84
|
+
It doesn't need Harbor installed. Run `agento11y harbor export --help` for the
|
|
85
|
+
flags:
|
|
86
|
+
|
|
87
|
+
- `--experiment-id`: the base experiment ID. The default is the Harbor job ID.
|
|
88
|
+
- `--suite-id`: the suite ID for the imported cases. The default is derived
|
|
89
|
+
from the dataset. On its own, it doesn't publish anything.
|
|
90
|
+
- `--publish-suite subset|replace`: store the job's tasks as a published suite
|
|
91
|
+
checkpoint before the import. `subset` adds and updates cases. `replace` also
|
|
92
|
+
removes stored cases that aren't in the job.
|
|
93
|
+
- `--main-reward`, `--pass-threshold`, and `--no-artifacts`: the same options
|
|
94
|
+
as the plugin.
|
|
95
|
+
|
|
96
|
+
From Python, call `publish_harbor_job`:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from agento11y.experiments import TestSuitesClient
|
|
100
|
+
from agento11y_harbor import publish_harbor_job
|
|
101
|
+
|
|
102
|
+
published = publish_harbor_job(
|
|
103
|
+
"jobs/my-job",
|
|
104
|
+
test_suites_client=TestSuitesClient(),
|
|
105
|
+
suite_publication_policy="subset",
|
|
106
|
+
)
|
|
107
|
+
for run in published.runs:
|
|
108
|
+
print(run.url)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Experiment IDs
|
|
112
|
+
|
|
113
|
+
The plugin and the importer use the same experiment ID:
|
|
114
|
+
`<experiment_id or Harbor job ID>-<candidate hash>`. The hash comes from the
|
|
115
|
+
agent name, model, and dataset, so it's the same for every run of the same
|
|
116
|
+
candidate.
|
|
117
|
+
|
|
118
|
+
A finalized experiment can't change. When you import a job, a candidate whose
|
|
119
|
+
experiment is already finalized, either live or by an earlier import, is
|
|
120
|
+
skipped with a warning, and the import fails if every candidate is. Pass a new
|
|
121
|
+
`--experiment-id` to publish the job as separate experiments. Resuming a
|
|
122
|
+
Harbor job also reuses the job ID, so pass a new `experiment_id` plugin option
|
|
123
|
+
when the job's experiments already finalized.
|
|
124
|
+
|
|
125
|
+
## Errors and retries
|
|
126
|
+
|
|
127
|
+
Publishing problems never stop the Harbor job. When a trial can't be
|
|
128
|
+
published, or one of its files can't be read, that trial is marked errored and
|
|
129
|
+
the rest of the job continues. The plugin logs a warning. The errored trial's
|
|
130
|
+
rewards stay diagnostic.
|
|
131
|
+
|
|
132
|
+
An agent timeout or non-zero exit doesn't error a trial when Harbor's verifier
|
|
133
|
+
still produced the main reward: like Harbor's own metrics, the reward decides
|
|
134
|
+
pass or fail, and the agent error is kept in the score metadata. Any other
|
|
135
|
+
Harbor exception, or a missing main reward, errors the trial.
|
|
136
|
+
|
|
137
|
+
Any errored trial finalizes its experiment as failed, and the experiment error
|
|
138
|
+
lists the errored trials. An importer skips a trial whose `result.json` it
|
|
139
|
+
can't read and logs a warning. Each candidate is imported on its own, so one
|
|
140
|
+
that fails to publish doesn't stop the others. The export command lists it and
|
|
141
|
+
exits with status 1, and rerunning the command publishes only what's missing.
|
|
142
|
+
|
|
143
|
+
If the plugin can't create an experiment, the candidate's next trial tries
|
|
144
|
+
again. Once it exists, the experiment is finalized as failed, because it's
|
|
145
|
+
missing the trials that started before then. If it never exists, the plugin logs
|
|
146
|
+
how many trials it left out when the job ends. Either way, export the job
|
|
147
|
+
directory to publish every trial, with a new `--experiment-id` if the
|
|
148
|
+
experiment exists.
|
|
149
|
+
|
|
150
|
+
If the Harbor process is interrupted, for example with Ctrl-C, the plugin marks
|
|
151
|
+
trials without a result errored and finalizes the experiments as failed. To
|
|
152
|
+
publish the finished trials, export the job directory with a new
|
|
153
|
+
`--experiment-id`.
|
|
154
|
+
|
|
155
|
+
Harbor retries a failed trial under the same trial name. The plugin keeps one
|
|
156
|
+
trial per name and publishes only the final attempt, so a failure that passes
|
|
157
|
+
on retry counts once, as a pass. The importer sees the same result, because
|
|
158
|
+
Harbor deletes the files of superseded attempts. Separate attempts from
|
|
159
|
+
`--n-attempts` are separate trials. Their attempt numbers follow the order
|
|
160
|
+
their agents start live and trial-name order on import, so compare them by experiment, not by
|
|
161
|
+
attempt number.
|
|
162
|
+
|
|
163
|
+
## Run trials from Python
|
|
164
|
+
|
|
165
|
+
`run_harbor` runs explicit Harbor `TrialConfig` objects for one agent and
|
|
166
|
+
model, one case at a time. It needs Harbor installed:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from agento11y_harbor import run_harbor
|
|
170
|
+
|
|
171
|
+
run = await run_harbor(suite, configs, name="Harbor regression", agent_env=otel_env)
|
|
172
|
+
print(run.url)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`configs` maps each suite case ID to a `harbor.models.trial.config.TrialConfig`.
|
|
176
|
+
The runner adds `agent_env`, `TRACEPARENT`, `TRACESTATE`, and the
|
|
177
|
+
`AGENTO11Y_*` trial identity to each agent's environment. A Python agent can
|
|
178
|
+
continue the trace and read the trial with `TrialRef.from_env()`:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
import os
|
|
182
|
+
from opentelemetry import context, propagate
|
|
183
|
+
|
|
184
|
+
carrier = {key.lower(): os.environ[key] for key in ("TRACEPARENT", "TRACESTATE") if key in os.environ}
|
|
185
|
+
token = context.attach(propagate.extract(carrier))
|
|
186
|
+
try:
|
|
187
|
+
run_instrumented_agent()
|
|
188
|
+
finally:
|
|
189
|
+
context.detach(token)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The live plugin can't add environment variables to agents, because Harbor
|
|
193
|
+
creates each agent before the plugin's hooks run. With the plugin, link
|
|
194
|
+
conversations through `AgentContext.metadata` instead.
|
|
195
|
+
|
|
196
|
+
`run_harbor` doesn't retry trials or run them concurrently. Use
|
|
197
|
+
`agento11y harbor` for Harbor's scheduler, retries, and datasets.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Harbor job import for Grafana Agent Observability."""
|
|
2
|
+
|
|
3
|
+
from .integration import PublishedHarborJob, PublishedHarborRun, publish_harbor_job
|
|
4
|
+
from .runner import run_harbor
|
|
5
|
+
|
|
6
|
+
__all__ = ["PublishedHarborJob", "PublishedHarborRun", "publish_harbor_job", "run_harbor"]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Command-line bridge used by ``agento11y harbor``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import sys
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
from agento11y.experiments import TestSuitesClient
|
|
12
|
+
|
|
13
|
+
from .integration import publish_harbor_job
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _export(argv: Sequence[str]) -> int:
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="agento11y harbor export",
|
|
19
|
+
description="Publish a completed Harbor job directory as one experiment per agent/model candidate.",
|
|
20
|
+
)
|
|
21
|
+
parser.add_argument("job_dir", help="Harbor job directory that contains the job result.json")
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"--experiment-id",
|
|
24
|
+
default="",
|
|
25
|
+
help="Base experiment ID (default: the Harbor job ID). A candidate hash is appended.",
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument("--name", default="", help="Base experiment name (default: Harbor <job directory name>)")
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"--suite-id",
|
|
30
|
+
default="",
|
|
31
|
+
help="Test suite ID for the imported cases (default: derived from the Harbor dataset). "
|
|
32
|
+
"Combine with --publish-suite to store the cases in that suite.",
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--publish-suite",
|
|
36
|
+
choices=("subset", "replace"),
|
|
37
|
+
default=None,
|
|
38
|
+
help="Publish the job's tasks as a stored suite checkpoint before importing. "
|
|
39
|
+
"subset adds or updates cases; replace also removes cases missing from the job. "
|
|
40
|
+
"Needs AGENTO11Y_GRAFANA_URL and AGENTO11Y_SERVICE_ACCOUNT_TOKEN.",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument("--main-reward", default="reward", help="Reward that decides pass or fail (default: reward)")
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--pass-threshold",
|
|
45
|
+
type=float,
|
|
46
|
+
default=1.0,
|
|
47
|
+
help="Minimum main reward that counts as a pass (default: 1.0)",
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument("--no-artifacts", action="store_true", help="Skip uploading trajectories and verifier logs")
|
|
50
|
+
args = parser.parse_args(list(argv))
|
|
51
|
+
try:
|
|
52
|
+
published = publish_harbor_job(
|
|
53
|
+
args.job_dir,
|
|
54
|
+
experiment_id=args.experiment_id,
|
|
55
|
+
experiment_name=args.name,
|
|
56
|
+
suite_id=args.suite_id,
|
|
57
|
+
test_suites_client=TestSuitesClient() if args.publish_suite else None,
|
|
58
|
+
suite_publication_policy=args.publish_suite,
|
|
59
|
+
main_reward=args.main_reward,
|
|
60
|
+
pass_threshold=args.pass_threshold,
|
|
61
|
+
upload_artifacts=not args.no_artifacts,
|
|
62
|
+
)
|
|
63
|
+
except ValueError as error:
|
|
64
|
+
print(f"agento11y: {error}", file=sys.stderr)
|
|
65
|
+
return 1
|
|
66
|
+
for run in published.runs:
|
|
67
|
+
print(run.url or run.experiment_id)
|
|
68
|
+
for label in published.skipped:
|
|
69
|
+
print(f"agento11y: skipped {label}: its experiment is already finalized", file=sys.stderr)
|
|
70
|
+
for failure in published.failed:
|
|
71
|
+
print(f"agento11y: could not publish {failure}", file=sys.stderr)
|
|
72
|
+
if published.failed:
|
|
73
|
+
print("agento11y: rerun the same command to publish only the missing experiments", file=sys.stderr)
|
|
74
|
+
return 1
|
|
75
|
+
return 0
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _run(argv: Sequence[str]) -> int:
|
|
79
|
+
harbor = shutil.which("harbor")
|
|
80
|
+
if harbor is None:
|
|
81
|
+
print("agento11y: Harbor is not installed or is not on PATH", file=sys.stderr)
|
|
82
|
+
print("Install Harbor, then retry this command.", file=sys.stderr)
|
|
83
|
+
return 1
|
|
84
|
+
args = list(argv)
|
|
85
|
+
if args[:1] == ["--"]:
|
|
86
|
+
args = args[1:]
|
|
87
|
+
plugins = [args[index + 1] for index, arg in enumerate(args[:-1]) if arg == "--plugin"]
|
|
88
|
+
plugins.extend(arg.removeprefix("--plugin=") for arg in args if arg.startswith("--plugin="))
|
|
89
|
+
if "agento11y" not in plugins:
|
|
90
|
+
args = ["--plugin", "agento11y", *args]
|
|
91
|
+
os.execvpe(harbor, [harbor, "run", *args], os.environ)
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
96
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
97
|
+
if not args or args[0] in {"-h", "--help", "help"}:
|
|
98
|
+
print(
|
|
99
|
+
"Usage:\n"
|
|
100
|
+
" agento11y harbor [run] -- <harbor run arguments>\n"
|
|
101
|
+
" agento11y harbor export <job-directory> [flags]\n"
|
|
102
|
+
"\n"
|
|
103
|
+
"Run 'agento11y harbor export --help' for export flags."
|
|
104
|
+
)
|
|
105
|
+
return 0
|
|
106
|
+
if args[0] == "export":
|
|
107
|
+
return _export(args[1:])
|
|
108
|
+
if args[0] == "run":
|
|
109
|
+
args = args[1:]
|
|
110
|
+
return _run(args)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def console_main() -> None:
|
|
114
|
+
raise SystemExit(main())
|