evalshift-sdk 0.2.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.
- evalshift_sdk-0.2.0/.github/workflows/ci.yml +37 -0
- evalshift_sdk-0.2.0/.github/workflows/release.yml +103 -0
- evalshift_sdk-0.2.0/.gitignore +24 -0
- evalshift_sdk-0.2.0/CHANGELOG.md +68 -0
- evalshift_sdk-0.2.0/DOCS.md +894 -0
- evalshift_sdk-0.2.0/LICENSE +21 -0
- evalshift_sdk-0.2.0/PKG-INFO +92 -0
- evalshift_sdk-0.2.0/README.md +63 -0
- evalshift_sdk-0.2.0/docs/DECISIONS.md +209 -0
- evalshift_sdk-0.2.0/docs/REDACTION.md +105 -0
- evalshift_sdk-0.2.0/docs/SCHEMA.md +185 -0
- evalshift_sdk-0.2.0/examples/support_agent/README.md +40 -0
- evalshift_sdk-0.2.0/examples/support_agent/agent.py +52 -0
- evalshift_sdk-0.2.0/examples/support_agent/evalshift.yaml +30 -0
- evalshift_sdk-0.2.0/examples/support_agent/fixtures.jsonl +4 -0
- evalshift_sdk-0.2.0/examples/support_agent/prompts.py +8 -0
- evalshift_sdk-0.2.0/examples/support_agent/tools.yaml +30 -0
- evalshift_sdk-0.2.0/llms-full.txt +330 -0
- evalshift_sdk-0.2.0/llms.txt +30 -0
- evalshift_sdk-0.2.0/pyproject.toml +87 -0
- evalshift_sdk-0.2.0/src/evalshift/__init__.py +46 -0
- evalshift_sdk-0.2.0/src/evalshift/adapters/langchain.py +554 -0
- evalshift_sdk-0.2.0/src/evalshift/capture/__init__.py +11 -0
- evalshift_sdk-0.2.0/src/evalshift/capture/api.py +660 -0
- evalshift_sdk-0.2.0/src/evalshift/capture/span.py +117 -0
- evalshift_sdk-0.2.0/src/evalshift/capture/state.py +60 -0
- evalshift_sdk-0.2.0/src/evalshift/config.py +221 -0
- evalshift_sdk-0.2.0/src/evalshift/hygiene/__init__.py +13 -0
- evalshift_sdk-0.2.0/src/evalshift/hygiene/dedup.py +42 -0
- evalshift_sdk-0.2.0/src/evalshift/hygiene/gc.py +72 -0
- evalshift_sdk-0.2.0/src/evalshift/hygiene/sample.py +32 -0
- evalshift_sdk-0.2.0/src/evalshift/py.typed +0 -0
- evalshift_sdk-0.2.0/src/evalshift/redaction/__init__.py +12 -0
- evalshift_sdk-0.2.0/src/evalshift/redaction/base.py +51 -0
- evalshift_sdk-0.2.0/src/evalshift/redaction/defaults.py +45 -0
- evalshift_sdk-0.2.0/src/evalshift/safety.py +49 -0
- evalshift_sdk-0.2.0/src/evalshift/sinks/__init__.py +9 -0
- evalshift_sdk-0.2.0/src/evalshift/sinks/base.py +27 -0
- evalshift_sdk-0.2.0/src/evalshift/sinks/file.py +68 -0
- evalshift_sdk-0.2.0/src/evalshift/sinks/hygiene.py +78 -0
- evalshift_sdk-0.2.0/src/evalshift/sinks/memory.py +45 -0
- evalshift_sdk-0.2.0/src/evalshift/trace/__init__.py +90 -0
- evalshift_sdk-0.2.0/src/evalshift/trace/migrate.py +456 -0
- evalshift_sdk-0.2.0/src/evalshift/trace/models.py +189 -0
- evalshift_sdk-0.2.0/src/evalshift/trace/schema.py +109 -0
- evalshift_sdk-0.2.0/src/evalshift/trace/serialize.py +328 -0
- evalshift_sdk-0.2.0/tests/__init__.py +0 -0
- evalshift_sdk-0.2.0/tests/adapters/__init__.py +0 -0
- evalshift_sdk-0.2.0/tests/adapters/test_langchain.py +233 -0
- evalshift_sdk-0.2.0/tests/conformance/__init__.py +0 -0
- evalshift_sdk-0.2.0/tests/conformance/cli_models_vendored.py +201 -0
- evalshift_sdk-0.2.0/tests/conformance/test_capture_conformance.py +88 -0
- evalshift_sdk-0.2.0/tests/conformance/test_migrate_parity.py +93 -0
- evalshift_sdk-0.2.0/tests/conformance/test_parity.py +187 -0
- evalshift_sdk-0.2.0/tests/conformance/test_serialize_parity.py +125 -0
- evalshift_sdk-0.2.0/tests/conftest.py +55 -0
- evalshift_sdk-0.2.0/tests/test_capture_agent.py +156 -0
- evalshift_sdk-0.2.0/tests/test_capture_async.py +186 -0
- evalshift_sdk-0.2.0/tests/test_capture_async_failopen.py +158 -0
- evalshift_sdk-0.2.0/tests/test_capture_concurrent.py +139 -0
- evalshift_sdk-0.2.0/tests/test_capture_failopen.py +220 -0
- evalshift_sdk-0.2.0/tests/test_capture_gate.py +34 -0
- evalshift_sdk-0.2.0/tests/test_capture_hygiene_e2e.py +87 -0
- evalshift_sdk-0.2.0/tests/test_capture_persistence_gate.py +122 -0
- evalshift_sdk-0.2.0/tests/test_capture_redaction.py +112 -0
- evalshift_sdk-0.2.0/tests/test_capture_sampling.py +130 -0
- evalshift_sdk-0.2.0/tests/test_capture_streaming.py +131 -0
- evalshift_sdk-0.2.0/tests/test_capture_tool.py +108 -0
- evalshift_sdk-0.2.0/tests/test_config.py +47 -0
- evalshift_sdk-0.2.0/tests/test_config_env_defaults.py +111 -0
- evalshift_sdk-0.2.0/tests/test_configure.py +169 -0
- evalshift_sdk-0.2.0/tests/test_filesink.py +75 -0
- evalshift_sdk-0.2.0/tests/test_hygiene_dedup.py +108 -0
- evalshift_sdk-0.2.0/tests/test_hygiene_gc.py +95 -0
- evalshift_sdk-0.2.0/tests/test_hygiene_sample.py +29 -0
- evalshift_sdk-0.2.0/tests/test_hygiene_sink.py +92 -0
- evalshift_sdk-0.2.0/tests/test_memory_sink.py +37 -0
- evalshift_sdk-0.2.0/tests/test_migrate.py +301 -0
- evalshift_sdk-0.2.0/tests/test_migrate_reconstruct.py +137 -0
- evalshift_sdk-0.2.0/tests/test_redaction.py +90 -0
- evalshift_sdk-0.2.0/tests/test_safety.py +52 -0
- evalshift_sdk-0.2.0/tests/test_serialize.py +329 -0
- evalshift_sdk-0.2.0/tests/test_sink_protocol.py +15 -0
- evalshift_sdk-0.2.0/tests/test_smoke.py +16 -0
- evalshift_sdk-0.2.0/tests/test_span.py +123 -0
- evalshift_sdk-0.2.0/tests/test_state.py +81 -0
- evalshift_sdk-0.2.0/tests/test_thread_safety.py +75 -0
- evalshift_sdk-0.2.0/uv.lock +1436 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
check:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v7
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
enable-cache: true
|
|
23
|
+
|
|
24
|
+
- name: Sync (dev group)
|
|
25
|
+
run: uv sync --locked
|
|
26
|
+
|
|
27
|
+
- name: Ruff lint
|
|
28
|
+
run: uv run ruff check
|
|
29
|
+
|
|
30
|
+
- name: Ruff format check
|
|
31
|
+
run: uv run ruff format --check
|
|
32
|
+
|
|
33
|
+
- name: Mypy (strict)
|
|
34
|
+
run: uv run mypy
|
|
35
|
+
|
|
36
|
+
- name: Pytest
|
|
37
|
+
run: uv run pytest
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
# Manual re-run for a tag whose release run failed or never fired. The version
|
|
7
|
+
# check still gates the publish, so this cannot ship a mismatched artifact.
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
inputs:
|
|
10
|
+
tag:
|
|
11
|
+
description: "Existing tag to release (e.g. v0.2.0)"
|
|
12
|
+
required: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
check:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v5
|
|
23
|
+
with:
|
|
24
|
+
# On a tag push this is the tag; on a manual dispatch it is the tag
|
|
25
|
+
# the operator named, so both paths build the same commit.
|
|
26
|
+
ref: ${{ inputs.tag || github.ref_name }}
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v7
|
|
30
|
+
with:
|
|
31
|
+
python-version: ${{ matrix.python-version }}
|
|
32
|
+
enable-cache: true
|
|
33
|
+
|
|
34
|
+
- name: Sync (dev group)
|
|
35
|
+
run: uv sync --locked
|
|
36
|
+
|
|
37
|
+
- name: Ruff lint
|
|
38
|
+
run: uv run ruff check
|
|
39
|
+
|
|
40
|
+
- name: Ruff format check
|
|
41
|
+
run: uv run ruff format --check
|
|
42
|
+
|
|
43
|
+
- name: Mypy (strict)
|
|
44
|
+
run: uv run mypy
|
|
45
|
+
|
|
46
|
+
- name: Pytest
|
|
47
|
+
run: uv run pytest
|
|
48
|
+
|
|
49
|
+
build:
|
|
50
|
+
needs: check
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v5
|
|
54
|
+
with:
|
|
55
|
+
# On a tag push this is the tag; on a manual dispatch it is the tag
|
|
56
|
+
# the operator named, so both paths build the same commit.
|
|
57
|
+
ref: ${{ inputs.tag || github.ref_name }}
|
|
58
|
+
|
|
59
|
+
- name: Install uv
|
|
60
|
+
uses: astral-sh/setup-uv@v7
|
|
61
|
+
with:
|
|
62
|
+
python-version: "3.14"
|
|
63
|
+
enable-cache: true
|
|
64
|
+
|
|
65
|
+
# The tag is the release's source of truth; refuse to publish a wheel whose
|
|
66
|
+
# version disagrees with it, which is how a v0.2.0 tag ships 0.1.0 artifacts.
|
|
67
|
+
- name: Verify tag matches package version
|
|
68
|
+
env:
|
|
69
|
+
RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
|
|
70
|
+
run: |
|
|
71
|
+
tag="${RELEASE_TAG#v}"
|
|
72
|
+
pkg="$(uv version --short)"
|
|
73
|
+
if [ "$tag" != "$pkg" ]; then
|
|
74
|
+
echo "::error::tag v$tag does not match package version $pkg"
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
echo "tag and package version agree: $pkg"
|
|
78
|
+
|
|
79
|
+
- name: Build
|
|
80
|
+
run: uv build
|
|
81
|
+
|
|
82
|
+
- name: Check metadata
|
|
83
|
+
run: uvx twine check dist/*
|
|
84
|
+
|
|
85
|
+
- uses: actions/upload-artifact@v4
|
|
86
|
+
with:
|
|
87
|
+
name: dist
|
|
88
|
+
path: dist/
|
|
89
|
+
|
|
90
|
+
publish:
|
|
91
|
+
needs: build
|
|
92
|
+
runs-on: ubuntu-latest
|
|
93
|
+
# Required for PyPI trusted publishing (OIDC). No API token is used.
|
|
94
|
+
permissions:
|
|
95
|
+
id-token: write
|
|
96
|
+
steps:
|
|
97
|
+
- uses: actions/download-artifact@v4
|
|
98
|
+
with:
|
|
99
|
+
name: dist
|
|
100
|
+
path: dist/
|
|
101
|
+
|
|
102
|
+
- name: Publish to PyPI
|
|
103
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Environments / tooling caches
|
|
10
|
+
.venv/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
|
|
15
|
+
# Capture output (the SDK writes here at runtime)
|
|
16
|
+
.evalshift/
|
|
17
|
+
|
|
18
|
+
# OS / editor
|
|
19
|
+
.DS_Store
|
|
20
|
+
|
|
21
|
+
# Local agent tooling and internal design notes (not part of the published SDK)
|
|
22
|
+
.claude/
|
|
23
|
+
.superpowers/
|
|
24
|
+
docs/superpowers/
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.2.0] - 2026-08-06
|
|
11
|
+
|
|
12
|
+
First release published to PyPI. Everything below describes the behavior of that
|
|
13
|
+
initial release rather than a delta from a previous one.
|
|
14
|
+
|
|
15
|
+
### Behavior
|
|
16
|
+
|
|
17
|
+
- **Capture is OFF by default.** Nothing is recorded or written unless `EVALSHIFT_CAPTURE`
|
|
18
|
+
is set to a truthy value, so instrumentation is safe to leave in production code paths.
|
|
19
|
+
|
|
20
|
+
- **Hygiene is ON by default (bounded but generous).** `dedup` defaults to `True` and
|
|
21
|
+
`max_captures` to `200` per suite directory, so a host that enables capture but never calls
|
|
22
|
+
`configure(...)` does not grow `.evalshift/captures/` without limit. `capture_ttl` and
|
|
23
|
+
`sample_rate` are off by default (`active_sink()` wraps in a `HygieneSink` unless every
|
|
24
|
+
hygiene knob is disabled). For fully unbounded capture, set
|
|
25
|
+
`EVALSHIFT_MAX_CAPTURES=0 EVALSHIFT_DEDUP=off` (or `configure(dedup=False, max_captures=None)`).
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Env vars now set the hygiene defaults, so no code change is needed to tune capture volume:
|
|
30
|
+
`EVALSHIFT_MAX_CAPTURES` (`0`/`none`/`unlimited` = uncapped), `EVALSHIFT_DEDUP`,
|
|
31
|
+
`EVALSHIFT_CAPTURE_TTL` (seconds), `EVALSHIFT_SAMPLE_RATE`. Precedence is
|
|
32
|
+
`configure(...)` > env var > built-in default; a malformed value fails open to the default.
|
|
33
|
+
|
|
34
|
+
- `configure(require_model_call=True)` opt-in persistence gate: when set, an agent
|
|
35
|
+
invocation that records no `model_call` span (a run that short-circuits before its
|
|
36
|
+
model call, or is cancelled beforehand) is dropped at `_finalize` instead of writing
|
|
37
|
+
a content-free capture. Off by default, so the write-per-sampled-invocation contract
|
|
38
|
+
and error-only telemetry are unchanged. Read via `config.require_model_call()`; the
|
|
39
|
+
predicate is `evalshift.capture.span.is_persistable(tree)`.
|
|
40
|
+
|
|
41
|
+
- `build_capture` gained optional `conversation_id` / `turn_index` /
|
|
42
|
+
`parent_capture_id` keyword parameters (schema 1.1.0), threaded through to
|
|
43
|
+
the emitted `CaptureEnvelope`. The public capture API — `_finalize`,
|
|
44
|
+
`@capture.agent(...)`, `capture.agent_session(...)`, and
|
|
45
|
+
`capture.agent_session_async(...)` — gained the same three keyword-only
|
|
46
|
+
parameters, all defaulting to `None` so existing callers are unaffected.
|
|
47
|
+
`agent_session` / `agent_session_async` are the recommended way to stamp
|
|
48
|
+
per-turn identity on a dynamic multi-turn conversation (fresh values per
|
|
49
|
+
`with` / `async with`); the `@capture.agent` decorator's values are static
|
|
50
|
+
per decoration.
|
|
51
|
+
- `input_hash` now folds `conversation_id` and `turn_index` into the hash
|
|
52
|
+
whenever `conversation_id` is set, so distinct turns of one conversation
|
|
53
|
+
that share identical `agent_input` text (e.g. "yes", "1pm") no longer
|
|
54
|
+
collapse under the `(suite, input_hash)` dedup key. When `conversation_id`
|
|
55
|
+
is unset, `input_hash` stays byte-identical to prior releases.
|
|
56
|
+
- `docs/SCHEMA.md` documents the 1.1.0 envelope fields, the messages-list
|
|
57
|
+
convention for `capture.model_call(input=...)` on multi-turn agents, and
|
|
58
|
+
why `input_hash` changes shape for conversation turns.
|
|
59
|
+
- `tests/conformance/cli_models_vendored.py` gained a vendored
|
|
60
|
+
`CaptureEnvelope` model (mirroring `evalshift-cli`'s
|
|
61
|
+
`evalshift.captures.models.CaptureEnvelope`, `extra="ignore"`) so the whole
|
|
62
|
+
envelope — not just the inner trace — validates against the CLI contract in
|
|
63
|
+
conformance tests.
|
|
64
|
+
- MIT `LICENSE` file. The SDK is imported into user applications, so it ships under
|
|
65
|
+
MIT rather than the AGPL-3.0-or-later used by the EvalShift server and CLI.
|
|
66
|
+
|
|
67
|
+
[Unreleased]: https://github.com/babaliauskas/evalshift-sdk/compare/v0.2.0...HEAD
|
|
68
|
+
[0.2.0]: https://github.com/babaliauskas/evalshift-sdk/releases/tag/v0.2.0
|