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.
Files changed (88) hide show
  1. evalshift_sdk-0.2.0/.github/workflows/ci.yml +37 -0
  2. evalshift_sdk-0.2.0/.github/workflows/release.yml +103 -0
  3. evalshift_sdk-0.2.0/.gitignore +24 -0
  4. evalshift_sdk-0.2.0/CHANGELOG.md +68 -0
  5. evalshift_sdk-0.2.0/DOCS.md +894 -0
  6. evalshift_sdk-0.2.0/LICENSE +21 -0
  7. evalshift_sdk-0.2.0/PKG-INFO +92 -0
  8. evalshift_sdk-0.2.0/README.md +63 -0
  9. evalshift_sdk-0.2.0/docs/DECISIONS.md +209 -0
  10. evalshift_sdk-0.2.0/docs/REDACTION.md +105 -0
  11. evalshift_sdk-0.2.0/docs/SCHEMA.md +185 -0
  12. evalshift_sdk-0.2.0/examples/support_agent/README.md +40 -0
  13. evalshift_sdk-0.2.0/examples/support_agent/agent.py +52 -0
  14. evalshift_sdk-0.2.0/examples/support_agent/evalshift.yaml +30 -0
  15. evalshift_sdk-0.2.0/examples/support_agent/fixtures.jsonl +4 -0
  16. evalshift_sdk-0.2.0/examples/support_agent/prompts.py +8 -0
  17. evalshift_sdk-0.2.0/examples/support_agent/tools.yaml +30 -0
  18. evalshift_sdk-0.2.0/llms-full.txt +330 -0
  19. evalshift_sdk-0.2.0/llms.txt +30 -0
  20. evalshift_sdk-0.2.0/pyproject.toml +87 -0
  21. evalshift_sdk-0.2.0/src/evalshift/__init__.py +46 -0
  22. evalshift_sdk-0.2.0/src/evalshift/adapters/langchain.py +554 -0
  23. evalshift_sdk-0.2.0/src/evalshift/capture/__init__.py +11 -0
  24. evalshift_sdk-0.2.0/src/evalshift/capture/api.py +660 -0
  25. evalshift_sdk-0.2.0/src/evalshift/capture/span.py +117 -0
  26. evalshift_sdk-0.2.0/src/evalshift/capture/state.py +60 -0
  27. evalshift_sdk-0.2.0/src/evalshift/config.py +221 -0
  28. evalshift_sdk-0.2.0/src/evalshift/hygiene/__init__.py +13 -0
  29. evalshift_sdk-0.2.0/src/evalshift/hygiene/dedup.py +42 -0
  30. evalshift_sdk-0.2.0/src/evalshift/hygiene/gc.py +72 -0
  31. evalshift_sdk-0.2.0/src/evalshift/hygiene/sample.py +32 -0
  32. evalshift_sdk-0.2.0/src/evalshift/py.typed +0 -0
  33. evalshift_sdk-0.2.0/src/evalshift/redaction/__init__.py +12 -0
  34. evalshift_sdk-0.2.0/src/evalshift/redaction/base.py +51 -0
  35. evalshift_sdk-0.2.0/src/evalshift/redaction/defaults.py +45 -0
  36. evalshift_sdk-0.2.0/src/evalshift/safety.py +49 -0
  37. evalshift_sdk-0.2.0/src/evalshift/sinks/__init__.py +9 -0
  38. evalshift_sdk-0.2.0/src/evalshift/sinks/base.py +27 -0
  39. evalshift_sdk-0.2.0/src/evalshift/sinks/file.py +68 -0
  40. evalshift_sdk-0.2.0/src/evalshift/sinks/hygiene.py +78 -0
  41. evalshift_sdk-0.2.0/src/evalshift/sinks/memory.py +45 -0
  42. evalshift_sdk-0.2.0/src/evalshift/trace/__init__.py +90 -0
  43. evalshift_sdk-0.2.0/src/evalshift/trace/migrate.py +456 -0
  44. evalshift_sdk-0.2.0/src/evalshift/trace/models.py +189 -0
  45. evalshift_sdk-0.2.0/src/evalshift/trace/schema.py +109 -0
  46. evalshift_sdk-0.2.0/src/evalshift/trace/serialize.py +328 -0
  47. evalshift_sdk-0.2.0/tests/__init__.py +0 -0
  48. evalshift_sdk-0.2.0/tests/adapters/__init__.py +0 -0
  49. evalshift_sdk-0.2.0/tests/adapters/test_langchain.py +233 -0
  50. evalshift_sdk-0.2.0/tests/conformance/__init__.py +0 -0
  51. evalshift_sdk-0.2.0/tests/conformance/cli_models_vendored.py +201 -0
  52. evalshift_sdk-0.2.0/tests/conformance/test_capture_conformance.py +88 -0
  53. evalshift_sdk-0.2.0/tests/conformance/test_migrate_parity.py +93 -0
  54. evalshift_sdk-0.2.0/tests/conformance/test_parity.py +187 -0
  55. evalshift_sdk-0.2.0/tests/conformance/test_serialize_parity.py +125 -0
  56. evalshift_sdk-0.2.0/tests/conftest.py +55 -0
  57. evalshift_sdk-0.2.0/tests/test_capture_agent.py +156 -0
  58. evalshift_sdk-0.2.0/tests/test_capture_async.py +186 -0
  59. evalshift_sdk-0.2.0/tests/test_capture_async_failopen.py +158 -0
  60. evalshift_sdk-0.2.0/tests/test_capture_concurrent.py +139 -0
  61. evalshift_sdk-0.2.0/tests/test_capture_failopen.py +220 -0
  62. evalshift_sdk-0.2.0/tests/test_capture_gate.py +34 -0
  63. evalshift_sdk-0.2.0/tests/test_capture_hygiene_e2e.py +87 -0
  64. evalshift_sdk-0.2.0/tests/test_capture_persistence_gate.py +122 -0
  65. evalshift_sdk-0.2.0/tests/test_capture_redaction.py +112 -0
  66. evalshift_sdk-0.2.0/tests/test_capture_sampling.py +130 -0
  67. evalshift_sdk-0.2.0/tests/test_capture_streaming.py +131 -0
  68. evalshift_sdk-0.2.0/tests/test_capture_tool.py +108 -0
  69. evalshift_sdk-0.2.0/tests/test_config.py +47 -0
  70. evalshift_sdk-0.2.0/tests/test_config_env_defaults.py +111 -0
  71. evalshift_sdk-0.2.0/tests/test_configure.py +169 -0
  72. evalshift_sdk-0.2.0/tests/test_filesink.py +75 -0
  73. evalshift_sdk-0.2.0/tests/test_hygiene_dedup.py +108 -0
  74. evalshift_sdk-0.2.0/tests/test_hygiene_gc.py +95 -0
  75. evalshift_sdk-0.2.0/tests/test_hygiene_sample.py +29 -0
  76. evalshift_sdk-0.2.0/tests/test_hygiene_sink.py +92 -0
  77. evalshift_sdk-0.2.0/tests/test_memory_sink.py +37 -0
  78. evalshift_sdk-0.2.0/tests/test_migrate.py +301 -0
  79. evalshift_sdk-0.2.0/tests/test_migrate_reconstruct.py +137 -0
  80. evalshift_sdk-0.2.0/tests/test_redaction.py +90 -0
  81. evalshift_sdk-0.2.0/tests/test_safety.py +52 -0
  82. evalshift_sdk-0.2.0/tests/test_serialize.py +329 -0
  83. evalshift_sdk-0.2.0/tests/test_sink_protocol.py +15 -0
  84. evalshift_sdk-0.2.0/tests/test_smoke.py +16 -0
  85. evalshift_sdk-0.2.0/tests/test_span.py +123 -0
  86. evalshift_sdk-0.2.0/tests/test_state.py +81 -0
  87. evalshift_sdk-0.2.0/tests/test_thread_safety.py +75 -0
  88. 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