trace2eval-cli 0.2.1__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 rfioly
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,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: trace2eval-cli
3
+ Version: 0.2.1
4
+ Summary: Turn production LLM traces into a regression eval set. Zero dependencies.
5
+ Author: rfioly
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/rfioly/trace2eval
8
+ Project-URL: Source, https://github.com/rfioly/trace2eval
9
+ Project-URL: Issues, https://github.com/rfioly/trace2eval/issues
10
+ Keywords: llm,evaluation,evals,observability,regression-testing,trace,ci
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Quality Assurance
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8.0; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ [English](README.en.md) | **简体中文**
30
+
31
+ # trace2eval
32
+
33
+ **把生产环境的 LLM 调用日志,变成一套回归测试集。**
34
+
35
+ 它是 Pytest 那个路子,区别在于用例不用手写——从你已经发生的线上调用里挖出来。
36
+
37
+ 零依赖、不用 API key、不调模型。同样的日志进,同样的用例出。
38
+
39
+ [![CI](https://github.com/rfioly/trace2eval/actions/workflows/ci.yml/badge.svg)](https://github.com/rfioly/trace2eval/actions/workflows/ci.yml)
40
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
41
+ [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](#)
42
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
43
+
44
+ ---
45
+
46
+ ## 快速开始
47
+
48
+ ```bash
49
+ pip install -e .
50
+
51
+ # 1. 把日志变成用例集
52
+ trace2eval build traces.jsonl -o evalset/
53
+
54
+ # 2. 拿用例集给一批输出打分
55
+ trace2eval run --cases evalset/cases.jsonl --outputs outputs.jsonl -o runs/current.json
56
+
57
+ # 3. 有回归就失败(退出码 1),可直接挂 CI
58
+ trace2eval check --baseline runs/baseline.json --current runs/current.json
59
+ ```
60
+
61
+ 需要 Python 3.10+。
62
+
63
+ ---
64
+
65
+ ## 三个命令
66
+
67
+ | 命令 | 做什么 |
68
+ | --- | --- |
69
+ | `build` | JSONL 调用日志 → 用例集(`cases.jsonl` + `report.md`) |
70
+ | `run` | 用例集 + 一批输出 → `metrics.json` |
71
+ | `check` | 两份指标对比 → 通过 / 失败 |
72
+
73
+ ---
74
+
75
+ ## 特性
76
+
77
+ - **零依赖,不用 API key。** 纯标准库,不调任何模型,完全离线可跑。
78
+ - **确定性。** 同一份日志产出同一套用例,逐字节一致——这条在 CI 里有断言。
79
+ - **一个问题一个用例。** 被问了 200 次的问题只变成一个用例,并且老实记录它代表 200 次调用。
80
+ - **形状从干净答案学,不从失败那条学。** 失败种子的长度下限来自同问题的干净答案中位数。
81
+ - **每个用例自检。** 报告里会标明哪些用例的检查**抓不住**它当初为什么坏,不混进"已覆盖"里。
82
+ - **相似度可换。** `--similarity module:function` 换成你自己的实现,包本身不引依赖。
83
+ - **天生适合 CI。** `check` 检出回归返回非零退出码。
84
+
85
+ ---
86
+
87
+ ## 跑一遍看看
88
+
89
+ ```console
90
+ $ trace2eval build examples/sample_traces.jsonl -o evalset
91
+ read 42 traces from examples/sample_traces.jsonl
92
+ generated 16 cases -> evalset/cases.jsonl
93
+ 8 collapsed as near-duplicates
94
+ 3 case(s) carry checks that cannot detect the failure they came from
95
+ report -> evalset/report.md
96
+ ```
97
+
98
+ 42 次调用 → 16 个用例。每个用例都记了为什么被挑中、形状从哪来、自检结论:
99
+
100
+ ```markdown
101
+ | Case | Score | In log | Shape from | Self-check | Input |
102
+ | -------- | ----- | ------ | ------------------------------ | ---------------------- | ------------------------ |
103
+ | case-001 | 8.0 | 1 | 日志的短答线(弱参考) | ok | 订单一直显示处理中,已经三天了 |
104
+ | case-007 | 4.5 | 6 | 同一问题的 5 个干净答案 | ok | 你们的退款政策是什么? |
105
+ | case-011 | 2.5 | 4 | 同一问题的 3 个干净答案 | failure_not_reproduced | 修改手机号 |
106
+ ```
107
+
108
+ 再看门禁。改一次提示词,修好一处、弄坏五处:
109
+
110
+ ```console
111
+ $ trace2eval check --baseline runs/baseline.json --current runs/current.json
112
+ | Metric | Baseline | Current | Rule |
113
+ | ----------------------- | -------- | ------- | ----------------------------------- |
114
+ | pass_rate | 1.0000 | 0.6875 | dropped by 0.3125 (allowed 0.0200) |
115
+ | format_compliance_rate | 1.0000 | 0.5000 | dropped by 0.5000 (allowed 0.0200) |
116
+ | fallback_rate | 0 | 0.0625 | rose by 0.0625 (allowed 0.0200) |
117
+
118
+ FAIL: 3 regression(s) detected
119
+ $ echo $?
120
+ 1
121
+ ```
122
+
123
+ 五个失败横跨三种失败模式:
124
+
125
+ ```
126
+ case-001: min_chars <- 答案被截断
127
+ case-002: not_fallback, min_chars <- 直接放弃作答
128
+ case-004: json <- 声明过的 JSON 契约没兑现
129
+ case-007: min_chars
130
+ case-011: min_chars
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 它怎么挑用例
136
+
137
+ ```mermaid
138
+ flowchart LR
139
+ A[JSONL 日志] --> B[解析与归一化]
140
+ B --> C[计算信号得分]
141
+ C --> D[近重复聚类]
142
+ D --> E{分数高于<br/>最低分?}
143
+ E -- 否 --> F[丢弃]
144
+ E -- 是 --> G[构建用例]
145
+ G --> H[cases.jsonl<br/>report.md]
146
+ H --> I[对输出评分]
147
+ I --> J[metrics.json]
148
+ J --> K{与基线对比}
149
+ K -- 有回归 --> L[退出码 1]
150
+ K -- 干净 --> M[退出码 0]
151
+ ```
152
+
153
+ 每条 trace 按信号加权重打分,分数决定谁被提拔。信号和权重就在 `signals.py` 顶部:
154
+
155
+ | 信号 | 权重 | 为什么它算信号 |
156
+ | --- | --- | --- |
157
+ | `negative_feedback` | 3.0 | 用户已经告诉你答错了。 |
158
+ | `empty_output` | 3.0 | 什么都没返回。 |
159
+ | `expected_shape_violated` | 3.0 | 声明过的契约(比如 JSON)没被满足。 |
160
+ | `user_retried` | 2.5 | 他问了两遍,说明第一次没解决。 |
161
+ | `fallback_phrase` | 2.0 | 模型放弃回答,而不是作答。 |
162
+ | `explicit_expectation` | 2.0 | 这条 trace 自己就带了断言。 |
163
+ | `output_much_shorter` | 1.5 | 大概率被截断,和日志中位数比。 |
164
+ | `output_much_longer` | 1.0 | 通常是不受控的长篇大论。 |
165
+ | `slow_response` | 1.0 | 超过整份日志的 p95 延迟。 |
166
+ | `expensive_call` | 1.0 | 超过整份日志的 p95 成本。 |
167
+
168
+ 设计取舍的完整论证在 [`DECISIONS.md`](DECISIONS.md)。
169
+
170
+ ---
171
+
172
+ ## 输入格式
173
+
174
+ 每行一个 JSON 对象,只有 `input` 和 `output` 必填,常见别名(`prompt`/`response`、`question`/`completion` 等)也认。
175
+
176
+ ```json
177
+ {
178
+ "id": "req-0042",
179
+ "input": "账单为什么变多了",
180
+ "output": "账单增加通常是因为套餐在到期后自动续费……",
181
+ "latency_ms": 830,
182
+ "cost_usd": 0.00027,
183
+ "feedback": "negative",
184
+ "retried": true,
185
+ "expect": { "json": true, "required": ["status"], "contains": ["已受理"] }
186
+ }
187
+ ```
188
+
189
+ 格式坏掉的行、没有输入的行会跳过并计数。空的 `output` 不算坏行——它是日志里最有价值的行之一。
190
+
191
+ ---
192
+
193
+ ## 已知边界
194
+
195
+ - **相似度是字符 n-gram**,不含相同字符的改写会被当成两个问题。可用 `--similarity` 换成你自己的实现。
196
+ - **行为型失败抓不住。** 样本日志 16 个用例里有 3 个,失败原因只是"用户又问了一遍",输出本身没毛病——任何对输出文本的确定性检查都抓不到。报告里已单独标出。
197
+ - **比较成本是 O(用例数 × 每簇不同问法数)**,不是 O(日志大小):同一问题问 5000 次只留 1 个待比对指纹。`MAX_DISTINCT_FINGERPRINTS = 512` 是拍的保护值,没压测过。
198
+ - **用例仍是提案**,但会自检:`report.md` 里单列"需要人看一眼"的用例。
199
+
200
+ ---
201
+
202
+ ## 仓库结构
203
+
204
+ ```
205
+ src/trace2eval/
206
+ schema.py trace 加载、字段别名、容错解析
207
+ signals.py 一条 trace 为什么值得测
208
+ select.py 相似度、聚类、用例构建
209
+ checks.py 确定性输出检查
210
+ runner.py 评分与回归对比
211
+ report.py markdown 渲染
212
+ matchers.py 可替换的相似度实现
213
+ tests/ 42 个测试
214
+ examples/ 一份 42 行样本日志 + 一次基线 / 一次回归运行
215
+ evalset/ 提交进仓库的构建产物
216
+ ```
217
+
218
+ ## 开发
219
+
220
+ ```bash
221
+ pip install -e ".[dev]"
222
+ pytest
223
+ ```
224
+
225
+ ## 许可证
226
+
227
+ MIT
@@ -0,0 +1,199 @@
1
+ [English](README.en.md) | **简体中文**
2
+
3
+ # trace2eval
4
+
5
+ **把生产环境的 LLM 调用日志,变成一套回归测试集。**
6
+
7
+ 它是 Pytest 那个路子,区别在于用例不用手写——从你已经发生的线上调用里挖出来。
8
+
9
+ 零依赖、不用 API key、不调模型。同样的日志进,同样的用例出。
10
+
11
+ [![CI](https://github.com/rfioly/trace2eval/actions/workflows/ci.yml/badge.svg)](https://github.com/rfioly/trace2eval/actions/workflows/ci.yml)
12
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
13
+ [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](#)
14
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
15
+
16
+ ---
17
+
18
+ ## 快速开始
19
+
20
+ ```bash
21
+ pip install -e .
22
+
23
+ # 1. 把日志变成用例集
24
+ trace2eval build traces.jsonl -o evalset/
25
+
26
+ # 2. 拿用例集给一批输出打分
27
+ trace2eval run --cases evalset/cases.jsonl --outputs outputs.jsonl -o runs/current.json
28
+
29
+ # 3. 有回归就失败(退出码 1),可直接挂 CI
30
+ trace2eval check --baseline runs/baseline.json --current runs/current.json
31
+ ```
32
+
33
+ 需要 Python 3.10+。
34
+
35
+ ---
36
+
37
+ ## 三个命令
38
+
39
+ | 命令 | 做什么 |
40
+ | --- | --- |
41
+ | `build` | JSONL 调用日志 → 用例集(`cases.jsonl` + `report.md`) |
42
+ | `run` | 用例集 + 一批输出 → `metrics.json` |
43
+ | `check` | 两份指标对比 → 通过 / 失败 |
44
+
45
+ ---
46
+
47
+ ## 特性
48
+
49
+ - **零依赖,不用 API key。** 纯标准库,不调任何模型,完全离线可跑。
50
+ - **确定性。** 同一份日志产出同一套用例,逐字节一致——这条在 CI 里有断言。
51
+ - **一个问题一个用例。** 被问了 200 次的问题只变成一个用例,并且老实记录它代表 200 次调用。
52
+ - **形状从干净答案学,不从失败那条学。** 失败种子的长度下限来自同问题的干净答案中位数。
53
+ - **每个用例自检。** 报告里会标明哪些用例的检查**抓不住**它当初为什么坏,不混进"已覆盖"里。
54
+ - **相似度可换。** `--similarity module:function` 换成你自己的实现,包本身不引依赖。
55
+ - **天生适合 CI。** `check` 检出回归返回非零退出码。
56
+
57
+ ---
58
+
59
+ ## 跑一遍看看
60
+
61
+ ```console
62
+ $ trace2eval build examples/sample_traces.jsonl -o evalset
63
+ read 42 traces from examples/sample_traces.jsonl
64
+ generated 16 cases -> evalset/cases.jsonl
65
+ 8 collapsed as near-duplicates
66
+ 3 case(s) carry checks that cannot detect the failure they came from
67
+ report -> evalset/report.md
68
+ ```
69
+
70
+ 42 次调用 → 16 个用例。每个用例都记了为什么被挑中、形状从哪来、自检结论:
71
+
72
+ ```markdown
73
+ | Case | Score | In log | Shape from | Self-check | Input |
74
+ | -------- | ----- | ------ | ------------------------------ | ---------------------- | ------------------------ |
75
+ | case-001 | 8.0 | 1 | 日志的短答线(弱参考) | ok | 订单一直显示处理中,已经三天了 |
76
+ | case-007 | 4.5 | 6 | 同一问题的 5 个干净答案 | ok | 你们的退款政策是什么? |
77
+ | case-011 | 2.5 | 4 | 同一问题的 3 个干净答案 | failure_not_reproduced | 修改手机号 |
78
+ ```
79
+
80
+ 再看门禁。改一次提示词,修好一处、弄坏五处:
81
+
82
+ ```console
83
+ $ trace2eval check --baseline runs/baseline.json --current runs/current.json
84
+ | Metric | Baseline | Current | Rule |
85
+ | ----------------------- | -------- | ------- | ----------------------------------- |
86
+ | pass_rate | 1.0000 | 0.6875 | dropped by 0.3125 (allowed 0.0200) |
87
+ | format_compliance_rate | 1.0000 | 0.5000 | dropped by 0.5000 (allowed 0.0200) |
88
+ | fallback_rate | 0 | 0.0625 | rose by 0.0625 (allowed 0.0200) |
89
+
90
+ FAIL: 3 regression(s) detected
91
+ $ echo $?
92
+ 1
93
+ ```
94
+
95
+ 五个失败横跨三种失败模式:
96
+
97
+ ```
98
+ case-001: min_chars <- 答案被截断
99
+ case-002: not_fallback, min_chars <- 直接放弃作答
100
+ case-004: json <- 声明过的 JSON 契约没兑现
101
+ case-007: min_chars
102
+ case-011: min_chars
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 它怎么挑用例
108
+
109
+ ```mermaid
110
+ flowchart LR
111
+ A[JSONL 日志] --> B[解析与归一化]
112
+ B --> C[计算信号得分]
113
+ C --> D[近重复聚类]
114
+ D --> E{分数高于<br/>最低分?}
115
+ E -- 否 --> F[丢弃]
116
+ E -- 是 --> G[构建用例]
117
+ G --> H[cases.jsonl<br/>report.md]
118
+ H --> I[对输出评分]
119
+ I --> J[metrics.json]
120
+ J --> K{与基线对比}
121
+ K -- 有回归 --> L[退出码 1]
122
+ K -- 干净 --> M[退出码 0]
123
+ ```
124
+
125
+ 每条 trace 按信号加权重打分,分数决定谁被提拔。信号和权重就在 `signals.py` 顶部:
126
+
127
+ | 信号 | 权重 | 为什么它算信号 |
128
+ | --- | --- | --- |
129
+ | `negative_feedback` | 3.0 | 用户已经告诉你答错了。 |
130
+ | `empty_output` | 3.0 | 什么都没返回。 |
131
+ | `expected_shape_violated` | 3.0 | 声明过的契约(比如 JSON)没被满足。 |
132
+ | `user_retried` | 2.5 | 他问了两遍,说明第一次没解决。 |
133
+ | `fallback_phrase` | 2.0 | 模型放弃回答,而不是作答。 |
134
+ | `explicit_expectation` | 2.0 | 这条 trace 自己就带了断言。 |
135
+ | `output_much_shorter` | 1.5 | 大概率被截断,和日志中位数比。 |
136
+ | `output_much_longer` | 1.0 | 通常是不受控的长篇大论。 |
137
+ | `slow_response` | 1.0 | 超过整份日志的 p95 延迟。 |
138
+ | `expensive_call` | 1.0 | 超过整份日志的 p95 成本。 |
139
+
140
+ 设计取舍的完整论证在 [`DECISIONS.md`](DECISIONS.md)。
141
+
142
+ ---
143
+
144
+ ## 输入格式
145
+
146
+ 每行一个 JSON 对象,只有 `input` 和 `output` 必填,常见别名(`prompt`/`response`、`question`/`completion` 等)也认。
147
+
148
+ ```json
149
+ {
150
+ "id": "req-0042",
151
+ "input": "账单为什么变多了",
152
+ "output": "账单增加通常是因为套餐在到期后自动续费……",
153
+ "latency_ms": 830,
154
+ "cost_usd": 0.00027,
155
+ "feedback": "negative",
156
+ "retried": true,
157
+ "expect": { "json": true, "required": ["status"], "contains": ["已受理"] }
158
+ }
159
+ ```
160
+
161
+ 格式坏掉的行、没有输入的行会跳过并计数。空的 `output` 不算坏行——它是日志里最有价值的行之一。
162
+
163
+ ---
164
+
165
+ ## 已知边界
166
+
167
+ - **相似度是字符 n-gram**,不含相同字符的改写会被当成两个问题。可用 `--similarity` 换成你自己的实现。
168
+ - **行为型失败抓不住。** 样本日志 16 个用例里有 3 个,失败原因只是"用户又问了一遍",输出本身没毛病——任何对输出文本的确定性检查都抓不到。报告里已单独标出。
169
+ - **比较成本是 O(用例数 × 每簇不同问法数)**,不是 O(日志大小):同一问题问 5000 次只留 1 个待比对指纹。`MAX_DISTINCT_FINGERPRINTS = 512` 是拍的保护值,没压测过。
170
+ - **用例仍是提案**,但会自检:`report.md` 里单列"需要人看一眼"的用例。
171
+
172
+ ---
173
+
174
+ ## 仓库结构
175
+
176
+ ```
177
+ src/trace2eval/
178
+ schema.py trace 加载、字段别名、容错解析
179
+ signals.py 一条 trace 为什么值得测
180
+ select.py 相似度、聚类、用例构建
181
+ checks.py 确定性输出检查
182
+ runner.py 评分与回归对比
183
+ report.py markdown 渲染
184
+ matchers.py 可替换的相似度实现
185
+ tests/ 42 个测试
186
+ examples/ 一份 42 行样本日志 + 一次基线 / 一次回归运行
187
+ evalset/ 提交进仓库的构建产物
188
+ ```
189
+
190
+ ## 开发
191
+
192
+ ```bash
193
+ pip install -e ".[dev]"
194
+ pytest
195
+ ```
196
+
197
+ ## 许可证
198
+
199
+ MIT
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ # The plain name "trace2eval" is taken on PyPI by an empty placeholder whose
7
+ # summary is literally "Reserved package" (621-byte sdist). Hence the -cli
8
+ # suffix on the distribution name. The console script is still "trace2eval",
9
+ # so the command users type is unchanged.
10
+ name = "trace2eval-cli"
11
+ version = "0.2.1"
12
+ description = "Turn production LLM traces into a regression eval set. Zero dependencies."
13
+ readme = "README.md"
14
+ requires-python = ">=3.10"
15
+ license = { text = "MIT" }
16
+ authors = [{ name = "rfioly" }]
17
+ keywords = [
18
+ "llm",
19
+ "evaluation",
20
+ "evals",
21
+ "observability",
22
+ "regression-testing",
23
+ "trace",
24
+ "ci",
25
+ ]
26
+ classifiers = [
27
+ "Development Status :: 4 - Beta",
28
+ "Intended Audience :: Developers",
29
+ "License :: OSI Approved :: MIT License",
30
+ "Operating System :: OS Independent",
31
+ "Programming Language :: Python :: 3",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13",
36
+ "Topic :: Software Development :: Quality Assurance",
37
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
38
+ ]
39
+ dependencies = []
40
+
41
+ [project.optional-dependencies]
42
+ dev = ["pytest>=8.0"]
43
+
44
+ [project.scripts]
45
+ trace2eval = "trace2eval.cli:main"
46
+
47
+ [project.urls]
48
+ Homepage = "https://github.com/rfioly/trace2eval"
49
+ Source = "https://github.com/rfioly/trace2eval"
50
+ Issues = "https://github.com/rfioly/trace2eval/issues"
51
+
52
+ [tool.setuptools.packages.find]
53
+ where = ["src"]
54
+
55
+ [tool.pytest.ini_options]
56
+ testpaths = ["tests"]
57
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,65 @@
1
+ """trace2eval -- turn production LLM traces into a regression eval set.
2
+
3
+ The gap this fills: tracing tools record what happened, and eval frameworks
4
+ score cases you already wrote. Nothing connects the two. The step in between --
5
+ deciding which recorded calls deserve to become permanent test cases -- is still
6
+ done by hand, by scrolling through logs.
7
+
8
+ from trace2eval import load_traces, select_cases
9
+
10
+ loaded = load_traces("traces.jsonl")
11
+ result = select_cases(loaded.traces, max_cases=25)
12
+ for case in result.cases:
13
+ print(case["id"], case["score"], case["input"])
14
+
15
+ Everything here is deterministic and offline. No model is called, no API key is
16
+ needed, and the same log always produces the same case set.
17
+ """
18
+
19
+ from .checks import CheckResult, detect_fallback, run_check, run_checks
20
+ from .matchers import char_bigram_jaccard, word_jaccard
21
+ from .runner import RunMetrics, compare_runs, load_outputs, run_cases
22
+ from .schema import Trace, load_traces, parse_trace
23
+ from .select import (
24
+ DEFAULT_DEDUP_THRESHOLD,
25
+ Matcher,
26
+ MatcherSpecError,
27
+ build_case,
28
+ load_matcher,
29
+ overlap_coefficient,
30
+ select_cases,
31
+ shingle_overlap,
32
+ )
33
+ from .signals import DEFAULT_WEIGHTS, Signal, build_context, compute_signals, score
34
+
35
+ __version__ = "0.2.1"
36
+
37
+ __all__ = [
38
+ "__version__",
39
+ "CheckResult",
40
+ "DEFAULT_DEDUP_THRESHOLD",
41
+ "DEFAULT_WEIGHTS",
42
+ "Matcher",
43
+ "MatcherSpecError",
44
+ "RunMetrics",
45
+ "Signal",
46
+ "Trace",
47
+ "build_case",
48
+ "build_context",
49
+ "char_bigram_jaccard",
50
+ "compare_runs",
51
+ "compute_signals",
52
+ "detect_fallback",
53
+ "load_matcher",
54
+ "load_outputs",
55
+ "load_traces",
56
+ "overlap_coefficient",
57
+ "parse_trace",
58
+ "run_check",
59
+ "run_checks",
60
+ "run_cases",
61
+ "score",
62
+ "select_cases",
63
+ "shingle_overlap",
64
+ "word_jaccard",
65
+ ]
@@ -0,0 +1,10 @@
1
+ """Allow ``python -m trace2eval`` as an alternative to the console script."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ from .cli import main
8
+
9
+ if __name__ == "__main__":
10
+ sys.exit(main())