sooth 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.
- sooth-0.2.1/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
- sooth-0.2.1/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
- sooth-0.2.1/.github/PULL_REQUEST_TEMPLATE.md +9 -0
- sooth-0.2.1/.github/workflows/ci.yml +21 -0
- sooth-0.2.1/.gitignore +10 -0
- sooth-0.2.1/LICENSE +21 -0
- sooth-0.2.1/PKG-INFO +110 -0
- sooth-0.2.1/README.md +87 -0
- sooth-0.2.1/docs/DESIGN.md +159 -0
- sooth-0.2.1/docs/DOGFOOD.md +18 -0
- sooth-0.2.1/docs/PRD.md +90 -0
- sooth-0.2.1/docs/TESTING.md +51 -0
- sooth-0.2.1/examples/calibration.json +39 -0
- sooth-0.2.1/examples/calibration.md +33 -0
- sooth-0.2.1/examples/draft.md +7 -0
- sooth-0.2.1/examples/news-1-evil.md +15 -0
- sooth-0.2.1/examples/news-1-output.md +25 -0
- sooth-0.2.1/examples/news-1-summ.md +30 -0
- sooth-0.2.1/examples/news-1.md +36 -0
- sooth-0.2.1/examples/policy.md +13 -0
- sooth-0.2.1/examples/pricing.md +11 -0
- sooth-0.2.1/examples/source.md +7 -0
- sooth-0.2.1/pyproject.toml +42 -0
- sooth-0.2.1/src/sooth/__init__.py +21 -0
- sooth-0.2.1/src/sooth/claims.py +59 -0
- sooth-0.2.1/src/sooth/cli.py +78 -0
- sooth-0.2.1/src/sooth/report.py +125 -0
- sooth-0.2.1/src/sooth/verify.py +266 -0
- sooth-0.2.1/tests/calibrate.py +74 -0
- sooth-0.2.1/tests/conftest.py +1 -0
- sooth-0.2.1/tests/smoke.sh +32 -0
- sooth-0.2.1/tests/test_core.py +236 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something is broken or a verdict is wrong
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**What happened**
|
|
8
|
+
|
|
9
|
+
**What you expected**
|
|
10
|
+
|
|
11
|
+
**Repro**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
sooth --source ... --text ...
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
(Attach the draft/source snippets if shareable — remove secrets first.)
|
|
18
|
+
|
|
19
|
+
**Environment**
|
|
20
|
+
|
|
21
|
+
- sooth version:
|
|
22
|
+
- Python version:
|
|
23
|
+
- OS:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: pip install -e ".[dev]"
|
|
20
|
+
- run: ruff check src tests
|
|
21
|
+
- run: pytest
|
sooth-0.2.1/.gitignore
ADDED
sooth-0.2.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Naufal Hilmiaji
|
|
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.
|
sooth-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sooth
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Verify AI-generated text against source material. Claim-by-claim trust reports powered by Jev (TypeSafe System One).
|
|
5
|
+
Project-URL: Homepage, https://github.com/naufalhilmiaji/sooth
|
|
6
|
+
Project-URL: Documentation, https://github.com/naufalhilmiaji/sooth/tree/main/docs
|
|
7
|
+
Author-email: Naufal Hilmiaji <nhilmiaji@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: ai,jev,llm,trust,typesafe,verification
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: typesafe-sdk
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Sooth
|
|
25
|
+
|
|
26
|
+
Verify AI-generated text against source material. Claim-by-claim **PASS / FAIL / REVIEW**, with calibrated probabilities you can act on in CI.
|
|
27
|
+
|
|
28
|
+
Built on [Jev](https://docs.typesafe.ai) (TypeSafe System One) — typed judgments and probabilities instead of generated prose. Every verdict shows its probability distribution, and the combination rules are plain code you can read. No black box.
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
sooth --source policy.md --text draft-reply.md
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Real output (planted errors in the draft vs a news source):
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
# Sooth
|
|
38
|
+
|
|
39
|
+
**PASS 2 · FAIL 3 · REVIEW 1 · UNCHECKABLE 1** — threshold 0.70
|
|
40
|
+
|
|
41
|
+
| # | Claim | Verdict | P | Why (P distribution) | Evidence |
|
|
42
|
+
|---|-------|---------|---|----------------------|----------|
|
|
43
|
+
| 3 | BNBR baru menuntaskan rights issue bernilai besar di harga Rp 33. | ❌ FAIL | ████████ 1.00 | contradicts 1.00 / not_found 0.00 / supports 0.00 · details 0.01 · missing #s: 33 | `examples/news-1.md:25` "Saham ini juga baru menyelesaikan rights issue dalam juml…" |
|
|
44
|
+
| 5 | BUMI hanya perlu turun sekitar 10% untuk menyentuh level Rp 50. | ❌ FAIL | ████████ 1.00 | contradicts 1.00 / not_found 0.00 / supports 0.00 · details 0.01 · missing #s: 10 | `examples/news-1.md:23` "Adapun PT Bumi Resources Tbk (BUMI) di sekitar Rp192 haru…" |
|
|
45
|
+
| 6 | BEI juga menetapkan batas atas harga saham Rp 5.000 per saham. | ⚠️ REVIEW | ████████ 1.00 | contradicts 0.00 / not_found 1.00 / supports 0.00 · details 0.02 · missing #s: 5.000 | |
|
|
46
|
+
| 7 | Para investor ritel sangat senang dengan aturan baru ini. | ➖ UNCHECKABLE | ██░░░░░░ 0.24 | P(checkable)=0.24 | |
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Why
|
|
50
|
+
|
|
51
|
+
AI writes fast, nobody checks. Claims ship wrong. Sooth checks each claim against the evidence you give it — and when it is unsure, it says `REVIEW` instead of guessing.
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install sooth # or: pip install -e ".[dev]" from source
|
|
57
|
+
export TYPESAFE_API_KEY=... # get one at console.typesafe.ai
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
sooth --source docs/policy.md --source tickets/t123.md --text draft-reply.md
|
|
64
|
+
|
|
65
|
+
# CI-friendly exit codes
|
|
66
|
+
sooth --source policy.md --text draft.md --format plain
|
|
67
|
+
# 0 = clean · 1 = any FAIL · 2 = any REVIEW · 3 = usage/config error
|
|
68
|
+
|
|
69
|
+
# Options
|
|
70
|
+
# --confidence T REVIEW below this confidence (default 0.7)
|
|
71
|
+
# --format md|plain
|
|
72
|
+
# -o FILE write report to file
|
|
73
|
+
# --log FILE append full judgment trace (one JSONL line per run)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## How it works
|
|
77
|
+
|
|
78
|
+
1. Draft is split into claims (one sentence each).
|
|
79
|
+
2. Each claim gets three questions to Jev, all fanned out in parallel batches: *is this checkable?*, *does the source support it?* (`supports` / `contradicts` / `not_found`), and *do all details match exactly?*
|
|
80
|
+
3. Verdicts are mapped in code: uncheckable → `UNCHECKABLE`; low confidence → `REVIEW`; then `PASS` / `FAIL`. Safeguards demote `PASS` to `REVIEW` when details drift or claim numbers are absent from the source (checked in plain code).
|
|
81
|
+
4. The report shows the full probability distribution per claim — not just a label.
|
|
82
|
+
|
|
83
|
+
The decision logic lives in [`src/sooth/verify.py`](src/sooth/verify.py) in a dozen readable lines. Change thresholds and rules there, not in prompts.
|
|
84
|
+
|
|
85
|
+
## Known limits (alpha)
|
|
86
|
+
|
|
87
|
+
- Safeguards catch most drift and smuggling: claim numbers absent from the source demote `PASS` to `REVIEW`, and a detail-match gate flags altered hedges ("about 74%" → "over 74%"). Not perfect — read the `Why` column before trusting a verdict.
|
|
88
|
+
- Derived numbers (totals, values computed outside the source) look "missing" and land in `REVIEW`.
|
|
89
|
+
- Source + questions must fit ~64k tokens — split long documents yourself.
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install -e ".[dev]"
|
|
95
|
+
python3 tests/test_core.py # pure checks, no network
|
|
96
|
+
pytest # same suite
|
|
97
|
+
bash tests/smoke.sh # live smoke (needs TYPESAFE_API_KEY)
|
|
98
|
+
PYTHONPATH=src python3 tests/calibrate.py # live calibration, 30 labeled claims
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Docs: [PRD](docs/PRD.md) · [Design](docs/DESIGN.md) · [Testing](docs/TESTING.md)
|
|
102
|
+
|
|
103
|
+
## Roadmap
|
|
104
|
+
|
|
105
|
+
- v0.2 shipped: source-span evidence (the exact source line behind each verdict, shown in the report)
|
|
106
|
+
- later: hosted web app — paste UI, history, team review queues
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT © Naufal Hilmiaji. Powered by [TypeSafe](https://typesafe.ai) / Jev.
|
sooth-0.2.1/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Sooth
|
|
2
|
+
|
|
3
|
+
Verify AI-generated text against source material. Claim-by-claim **PASS / FAIL / REVIEW**, with calibrated probabilities you can act on in CI.
|
|
4
|
+
|
|
5
|
+
Built on [Jev](https://docs.typesafe.ai) (TypeSafe System One) — typed judgments and probabilities instead of generated prose. Every verdict shows its probability distribution, and the combination rules are plain code you can read. No black box.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
sooth --source policy.md --text draft-reply.md
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Real output (planted errors in the draft vs a news source):
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
# Sooth
|
|
15
|
+
|
|
16
|
+
**PASS 2 · FAIL 3 · REVIEW 1 · UNCHECKABLE 1** — threshold 0.70
|
|
17
|
+
|
|
18
|
+
| # | Claim | Verdict | P | Why (P distribution) | Evidence |
|
|
19
|
+
|---|-------|---------|---|----------------------|----------|
|
|
20
|
+
| 3 | BNBR baru menuntaskan rights issue bernilai besar di harga Rp 33. | ❌ FAIL | ████████ 1.00 | contradicts 1.00 / not_found 0.00 / supports 0.00 · details 0.01 · missing #s: 33 | `examples/news-1.md:25` "Saham ini juga baru menyelesaikan rights issue dalam juml…" |
|
|
21
|
+
| 5 | BUMI hanya perlu turun sekitar 10% untuk menyentuh level Rp 50. | ❌ FAIL | ████████ 1.00 | contradicts 1.00 / not_found 0.00 / supports 0.00 · details 0.01 · missing #s: 10 | `examples/news-1.md:23` "Adapun PT Bumi Resources Tbk (BUMI) di sekitar Rp192 haru…" |
|
|
22
|
+
| 6 | BEI juga menetapkan batas atas harga saham Rp 5.000 per saham. | ⚠️ REVIEW | ████████ 1.00 | contradicts 0.00 / not_found 1.00 / supports 0.00 · details 0.02 · missing #s: 5.000 | |
|
|
23
|
+
| 7 | Para investor ritel sangat senang dengan aturan baru ini. | ➖ UNCHECKABLE | ██░░░░░░ 0.24 | P(checkable)=0.24 | |
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Why
|
|
27
|
+
|
|
28
|
+
AI writes fast, nobody checks. Claims ship wrong. Sooth checks each claim against the evidence you give it — and when it is unsure, it says `REVIEW` instead of guessing.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install sooth # or: pip install -e ".[dev]" from source
|
|
34
|
+
export TYPESAFE_API_KEY=... # get one at console.typesafe.ai
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
sooth --source docs/policy.md --source tickets/t123.md --text draft-reply.md
|
|
41
|
+
|
|
42
|
+
# CI-friendly exit codes
|
|
43
|
+
sooth --source policy.md --text draft.md --format plain
|
|
44
|
+
# 0 = clean · 1 = any FAIL · 2 = any REVIEW · 3 = usage/config error
|
|
45
|
+
|
|
46
|
+
# Options
|
|
47
|
+
# --confidence T REVIEW below this confidence (default 0.7)
|
|
48
|
+
# --format md|plain
|
|
49
|
+
# -o FILE write report to file
|
|
50
|
+
# --log FILE append full judgment trace (one JSONL line per run)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## How it works
|
|
54
|
+
|
|
55
|
+
1. Draft is split into claims (one sentence each).
|
|
56
|
+
2. Each claim gets three questions to Jev, all fanned out in parallel batches: *is this checkable?*, *does the source support it?* (`supports` / `contradicts` / `not_found`), and *do all details match exactly?*
|
|
57
|
+
3. Verdicts are mapped in code: uncheckable → `UNCHECKABLE`; low confidence → `REVIEW`; then `PASS` / `FAIL`. Safeguards demote `PASS` to `REVIEW` when details drift or claim numbers are absent from the source (checked in plain code).
|
|
58
|
+
4. The report shows the full probability distribution per claim — not just a label.
|
|
59
|
+
|
|
60
|
+
The decision logic lives in [`src/sooth/verify.py`](src/sooth/verify.py) in a dozen readable lines. Change thresholds and rules there, not in prompts.
|
|
61
|
+
|
|
62
|
+
## Known limits (alpha)
|
|
63
|
+
|
|
64
|
+
- Safeguards catch most drift and smuggling: claim numbers absent from the source demote `PASS` to `REVIEW`, and a detail-match gate flags altered hedges ("about 74%" → "over 74%"). Not perfect — read the `Why` column before trusting a verdict.
|
|
65
|
+
- Derived numbers (totals, values computed outside the source) look "missing" and land in `REVIEW`.
|
|
66
|
+
- Source + questions must fit ~64k tokens — split long documents yourself.
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install -e ".[dev]"
|
|
72
|
+
python3 tests/test_core.py # pure checks, no network
|
|
73
|
+
pytest # same suite
|
|
74
|
+
bash tests/smoke.sh # live smoke (needs TYPESAFE_API_KEY)
|
|
75
|
+
PYTHONPATH=src python3 tests/calibrate.py # live calibration, 30 labeled claims
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Docs: [PRD](docs/PRD.md) · [Design](docs/DESIGN.md) · [Testing](docs/TESTING.md)
|
|
79
|
+
|
|
80
|
+
## Roadmap
|
|
81
|
+
|
|
82
|
+
- v0.2 shipped: source-span evidence (the exact source line behind each verdict, shown in the report)
|
|
83
|
+
- later: hosted web app — paste UI, history, team review queues
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT © Naufal Hilmiaji. Powered by [TypeSafe](https://typesafe.ai) / Jev.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Technical Design — Sooth v0.1
|
|
2
|
+
|
|
3
|
+
Companion to `PRD.md`. Stack and data flow only. No speculative layers.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
- Python 3.11+, official `typesafe-sdk` package (`TypeSafeClient.system_one`), stdlib otherwise (argparse, re, json, pathlib).
|
|
8
|
+
- No web framework, no DB, no plugin system. Four modules + CLI entry is enough.
|
|
9
|
+
|
|
10
|
+
## Layout (src layout, standard packaging)
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
src/sooth/
|
|
14
|
+
__init__.py # package API
|
|
15
|
+
claims.py # split text → claims (pure)
|
|
16
|
+
verify.py # build Jev questions, call API, map answers → verdicts (pure map + one I/O call)
|
|
17
|
+
report.py # verdicts → markdown / plain text + JSONL log record (pure)
|
|
18
|
+
cli.py # argparse, file I/O, exit codes, --log writer
|
|
19
|
+
tests/
|
|
20
|
+
conftest.py
|
|
21
|
+
test_core.py # asserts for pure functions + verdict mapping
|
|
22
|
+
smoke.sh # optional live smoke (real key)
|
|
23
|
+
examples/ # fixture: source.md, draft.md
|
|
24
|
+
docs/ # PRD, DESIGN, TESTING
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Flow
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
sources + draft
|
|
31
|
+
→ claims.split(draft) → list[Claim]
|
|
32
|
+
→ verify.batch(claims, sources) → list[Verdict] # 1..n Jev calls
|
|
33
|
+
→ report.render(verdicts) → markdown string
|
|
34
|
+
→ stdout / -o file
|
|
35
|
+
→ optional JSONL log append
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Pure functions everywhere except `verify.call_jev()` and file I/O. Testable without network.
|
|
39
|
+
|
|
40
|
+
## Claim splitting (`claims.py`)
|
|
41
|
+
|
|
42
|
+
v0.1: sentence splitter on `.`/`!`/`?` + newline, keeping numbers intact (`3.5 days`). Drop empties and pure questions? No — questions are claims too ("Does it support X?" is checkable as written intent… actually drop interrogatives and headings, they are not claims). Rules:
|
|
43
|
+
|
|
44
|
+
- Keep: declarative sentences, ≥ 4 words.
|
|
45
|
+
- Skip: headings (`# …`), list bullets' leading markers kept as text, sentences < 4 words, lines that end with `?`.
|
|
46
|
+
- Each claim keeps `text` + `source_span` = `{file, line}` of origin (for "claim #3 came from draft line 12").
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
# ponytail: regex splitter, mis-splits quotes/abbreviations; upgrade to clause-level when real drafts demand
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Jev request (`verify.py`)
|
|
53
|
+
|
|
54
|
+
One request per batch of ≤ 30 claims. State is shared; questions reference claims by path.
|
|
55
|
+
|
|
56
|
+
```jsonc
|
|
57
|
+
// state
|
|
58
|
+
{
|
|
59
|
+
"sources": [
|
|
60
|
+
{ "name": "policy.md", "text": "…" }, // concatenated if many; name kept for evidence line
|
|
61
|
+
{ "name": "tickets/t123.md", "text": "…" }
|
|
62
|
+
],
|
|
63
|
+
"claims": [
|
|
64
|
+
{ "id": "c1", "text": "Refunds are processed in 3 days." },
|
|
65
|
+
{ "id": "c2", "text": "We support Bitcoin." }
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Per claim, **four parallel questions** (fan-out pattern; statement text is embedded in the instructions):
|
|
71
|
+
|
|
72
|
+
```jsonc
|
|
73
|
+
"c1_checkable": {
|
|
74
|
+
"type": "noul",
|
|
75
|
+
"instructions": "Statement: <claim text>\n\nIs the statement a concrete factual claim that the evidence in `sources` could support or contradict? ...",
|
|
76
|
+
// noul answer = P(checkable)
|
|
77
|
+
},
|
|
78
|
+
"c1_verdict": {
|
|
79
|
+
"type": "choice",
|
|
80
|
+
"instructions": "Statement: <claim text>\n\nDoes the evidence in `sources` support the statement?",
|
|
81
|
+
"criteria": {
|
|
82
|
+
"supports": "...",
|
|
83
|
+
"contradicts": "...",
|
|
84
|
+
"not_found": "..."
|
|
85
|
+
}
|
|
86
|
+
// choice + probabilities + confidence
|
|
87
|
+
},
|
|
88
|
+
"c1_details": {
|
|
89
|
+
"type": "noul",
|
|
90
|
+
"instructions": "Statement: <claim text>\n\nDoes EVERY specific detail — names, numbers, dates, comparisons such as 'more than' or 'about' — exactly match the evidence in `sources`? ...",
|
|
91
|
+
// noul answer = P(all details match)
|
|
92
|
+
},
|
|
93
|
+
"c1_evidence": {
|
|
94
|
+
"type": "choice",
|
|
95
|
+
"instructions": "Statement: <claim text>\n\nWhich candidate segment best supports or contradicts the statement? Full text of each id is in `segments`. Choose 'none' if no segment is relevant.",
|
|
96
|
+
"criteria": { "s12": "<first 80 chars of candidate>", "...": "…", "none": "No segment is relevant to the statement." }
|
|
97
|
+
// code pre-filters ~6 candidate ids per claim (word overlap + number hits)
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- 30 claims → 120 questions, one call. Over batch cap or 422 → split and retry half (SDK retries 429/529 already).
|
|
102
|
+
- Pin model `jev-1.13.0` (thresholds tuned against it). Constant in `verify.py`.
|
|
103
|
+
- API key: env `TYPESAFE_API_KEY`. Missing → exit 3 with one-line hint.
|
|
104
|
+
|
|
105
|
+
## Verdict mapping (pure)
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
if p_checkable < 0.5: → UNCHECKABLE
|
|
109
|
+
elif confidence < threshold: → REVIEW
|
|
110
|
+
elif choice == "supports": → PASS
|
|
111
|
+
elif choice == "contradicts": → FAIL
|
|
112
|
+
else: → REVIEW # not_found
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Then safeguards (`apply_safeguards`, pure) — demote `PASS` → `REVIEW` when:
|
|
116
|
+
|
|
117
|
+
- `details_p < 0.5` (detail-gate Noul says some detail drifted), or
|
|
118
|
+
- `missing_numbers(claim, sources)` non-empty — claim numbers absent from every source (regex extraction, separator-normalized; no model involved).
|
|
119
|
+
|
|
120
|
+
FAIL/REVIEW/UNCHECKABLE pass through untouched.
|
|
121
|
+
|
|
122
|
+
`Verdict = {claim_id, claim_text, draft_line, kind, p_checkable?, choice?, probabilities, confidence, details_p?, missing_numbers}`
|
|
123
|
+
|
|
124
|
+
Evidence (v0.2, pre-parsed selection pattern): sources are split into sentence `segments`; code ranks ~6 candidates per claim (word overlap, numbers weighted ×3); a per-claim Choice selects the best span (`none` allowed). Report shows `source:line` + snippet. Best-effort — `none` is valid when no span matches.
|
|
125
|
+
|
|
126
|
+
(If PRD table showed source snippets — that is v0.2. v0.1 report substitutes distribution; PRD example is target UX.)
|
|
127
|
+
|
|
128
|
+
## Report (`report.py`)
|
|
129
|
+
|
|
130
|
+
Markdown: summary line (`PASS n · FAIL n · REVIEW n · UNCHECKABLE n`), then one table as in PRD, ASCII probability bar in P column (`██████░░ 0.91` is enough; no unicode-only requirement). FAIL rows first? No — keep draft order, print summary counts first. `REVIEW` and `UNCHECKABLE` share a section below the table if any.
|
|
131
|
+
|
|
132
|
+
## Decision log (`--log`)
|
|
133
|
+
|
|
134
|
+
Append-only JSONL, one object per run (not per claim — one line, pretty fields):
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{"ts": "…", "model": "jev-1.13.0", "threshold": 0.7, "sources": ["policy.md"], "draft": "draft-reply.md", "usage": {"input_tokens": 0, "output_tokens": 0}, "results": [ …verdicts with full probabilities… ]}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This is the decision-ledger seed (audit trail). No viewer in v0.1.
|
|
141
|
+
|
|
142
|
+
## Exit codes
|
|
143
|
+
|
|
144
|
+
| Code | Meaning |
|
|
145
|
+
|------|---------|
|
|
146
|
+
| 0 | all PASS or UNCHECKABLE only |
|
|
147
|
+
| 1 | any FAIL |
|
|
148
|
+
| 2 | any REVIEW (no FAIL) |
|
|
149
|
+
| 3 | usage / missing key / unreadable file |
|
|
150
|
+
|
|
151
|
+
## Errors
|
|
152
|
+
|
|
153
|
+
- SDK raises typed errors; catch at CLI boundary, print one line to stderr, exit 3 (or 2 on API failure mid-run? → exit 3, "could not verify", never print a partial report marked complete).
|
|
154
|
+
- Empty draft → usage error. Empty sources → usage error (nothing to check against).
|
|
155
|
+
|
|
156
|
+
## Security
|
|
157
|
+
|
|
158
|
+
- Key via env only, never logged, never in `--log`.
|
|
159
|
+
- Files read as UTF-8 text. No shell-out, no eval. `--log` path user-controlled write (documented).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Dogfood log — week of 2026-09-25
|
|
2
|
+
|
|
3
|
+
Run Sooth on your own real AI output before building anything new. One row per session. Findings here drive Phase 4/5 decisions.
|
|
4
|
+
|
|
5
|
+
## Loop (5 min per draft)
|
|
6
|
+
|
|
7
|
+
1. Take an AI draft you were about to trust (reply, summary, PR description, doc).
|
|
8
|
+
2. `sooth --source <the ground truth> --text <the draft> --log dogfood.jsonl`
|
|
9
|
+
3. Read the report. Mark each verdict right/wrong by your own eyes.
|
|
10
|
+
4. Log one row below.
|
|
11
|
+
|
|
12
|
+
| Date | Draft type | Verdicts right/wrong | Surprise (if any) | Want next |
|
|
13
|
+
|------|------------|----------------------|-------------------|-----------|
|
|
14
|
+
| | | | | |
|
|
15
|
+
|
|
16
|
+
## Question this answers
|
|
17
|
+
|
|
18
|
+
Does anyone actually paste ground truth + draft and act on the report? If the tool dies on real drafts (formatting, length, language), Phase 4 chunking/URL input jumps the queue. If the report is ignored even when right, the web app idea is wrong.
|
sooth-0.2.1/docs/PRD.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# PRD — Sooth (v0.1)
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
AI writes text fast. Nobody checks it. Wrong claims ship. In 2026 most drafts (support replies, docs, reports, PR descriptions) are AI-written and reviewed by skimming humans.
|
|
6
|
+
|
|
7
|
+
Sooth is a cheap, automatic checker: does each claim in the AI draft actually follow from the source material?
|
|
8
|
+
|
|
9
|
+
It targets the AI-era verification gap: AI writes fast, nobody checks before shipping. Opaque AI decisions are addressed by design: every verdict shows its probability distribution, and `--log` records the full judgment trace.
|
|
10
|
+
|
|
11
|
+
## What it is
|
|
12
|
+
|
|
13
|
+
CLI tool. Input: source text(s) + AI-generated draft. Output: a **trust report** — every claim marked `PASS` / `FAIL` / `REVIEW`, with probability, reason, and source snippet.
|
|
14
|
+
|
|
15
|
+
Engine by [Jev](https://docs.typesafe.ai) (TypeSafe System One): typed judgments + calibrated probabilities, not generated prose.
|
|
16
|
+
|
|
17
|
+
## Who it is for (v0.1)
|
|
18
|
+
|
|
19
|
+
Developers and technical teams who already work in a terminal and pipe AI output into things: CI jobs, agent pipelines, release notes, support macros.
|
|
20
|
+
|
|
21
|
+
## User story
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
sooth --source docs/policy.md --source tickets/t123.md --text draft-reply.md
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
1. User gives sources (ground truth) and draft (AI text to verify).
|
|
28
|
+
2. Tool splits draft into claims (sentence per claim in v0.1).
|
|
29
|
+
3. Each claim judged against sources: supported / contradicted / not found — plus "is this even checkable?".
|
|
30
|
+
4. Report printed as markdown. Low-confidence claims land in `REVIEW` instead of fake-precise verdicts.
|
|
31
|
+
5. Optional `--log decisions.jsonl` appends full judgment trace (audit trail seed).
|
|
32
|
+
|
|
33
|
+
## Report shape (contract)
|
|
34
|
+
|
|
35
|
+
| # | Claim | Verdict | P | Why (v0.1) |
|
|
36
|
+
|---|-------|---------|---|------------|
|
|
37
|
+
| 1 | Refunds are processed in 3 days. | ✅ PASS | 0.91 | supports .91 / contradicts .02 / not_found .07 |
|
|
38
|
+
| 2 | We support Bitcoin. | ❌ FAIL | 0.87 | supports .03 / contradicts .87 / not_found .10 |
|
|
39
|
+
| 3 | Customers love the change. | ⚠️ REVIEW | 0.54 | not found in sources |
|
|
40
|
+
|
|
41
|
+
"Why" = probability distribution. Evidence = the source span the model selected (`source:line` + snippet).
|
|
42
|
+
|
|
43
|
+
Verdict rules (fixed, readable in code):
|
|
44
|
+
|
|
45
|
+
- claim not checkable (opinion, vague) → `UNCHECKABLE` (listed under REVIEW section)
|
|
46
|
+
- `supports` + confidence ≥ threshold → `PASS`
|
|
47
|
+
- `contradicts` + confidence ≥ threshold → `FAIL`
|
|
48
|
+
- `not in source` **or** confidence < threshold → `REVIEW`
|
|
49
|
+
|
|
50
|
+
Default threshold 0.7, flag `--confidence`.
|
|
51
|
+
|
|
52
|
+
## MVP scope
|
|
53
|
+
|
|
54
|
+
In:
|
|
55
|
+
|
|
56
|
+
- CLI: `sooth --source … [--source …] --text … [--confidence T] [--log FILE] [--format md|plain]`
|
|
57
|
+
- Plain text / markdown input (UTF-8). Multiple sources.
|
|
58
|
+
- Sentence-per-claim splitting.
|
|
59
|
+
- Jev fan-out verification (batched).
|
|
60
|
+
- Markdown report to stdout (or `-o FILE`).
|
|
61
|
+
- JSONL decision log (`--log`).
|
|
62
|
+
- Exit codes for CI: `0` all PASS/CLEAN, `1` any FAIL, `2` any REVIEW remaining, `3` usage/config error.
|
|
63
|
+
|
|
64
|
+
Out (v0.1 — later or never):
|
|
65
|
+
|
|
66
|
+
- Web UI (paid phase), PDF/HTML parsing, images
|
|
67
|
+
- Smarter claim splitting (clauses, bullet merging)
|
|
68
|
+
- Team features, history, webhooks
|
|
69
|
+
- Non-English tuning (Jev is English-strongest)
|
|
70
|
+
- Custom question editing UI
|
|
71
|
+
|
|
72
|
+
## Success criteria (v0.1 done when)
|
|
73
|
+
|
|
74
|
+
1. `sooth` runs end-to-end against a sample fixture and prints the report above.
|
|
75
|
+
2. On a hand-labeled set of 30 claims (10 supported / 10 contradicted / 10 absent-or-fluff): ≥ 24 correct verdicts after threshold tuning, no confident (≥0.7) wrong `PASS` on a contradicted claim in the set.
|
|
76
|
+
3. CI-usable: exit codes behave; works without network when `--log`-replaying a fixture later (replay is stretch).
|
|
77
|
+
4. One new user understands the output with no explanation beyond `--help`.
|
|
78
|
+
|
|
79
|
+
## Monetization (later — not built now)
|
|
80
|
+
|
|
81
|
+
Free OSS: CLI + library. Paid web app later: paste UI, history, team folders, "verify before publish" API. Same engine. No paywall logic in v0.1.
|
|
82
|
+
|
|
83
|
+
## Open decisions (defaults chosen, reversible)
|
|
84
|
+
|
|
85
|
+
| Decision | Default | Note |
|
|
86
|
+
|----------|---------|------|
|
|
87
|
+
| Language | Python 3.11+ | official Jev Python SDK |
|
|
88
|
+
| Claim split | sentence regex | upgrade when it mis-splits real drafts |
|
|
89
|
+
| Batch size | 30 claims/request | stay well under 64k token cap |
|
|
90
|
+
| REVIEW exit code | 2 | CI can choose to ignore |
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Tests & Verification Plan — Sooth v0.1
|
|
2
|
+
|
|
3
|
+
Three layers: fast pure checks (always), live API smoke (optional), accuracy calibration (human, before release). Matches `DESIGN.md` module split.
|
|
4
|
+
|
|
5
|
+
## 1. Unit / pure checks — `tests/test_core.py`
|
|
6
|
+
|
|
7
|
+
One file, plain asserts, run with `python -m tests.test_core` or pytest if already installed. No network. Covers the logic that would silently corrupt verdicts:
|
|
8
|
+
|
|
9
|
+
| Area | Cases (assert each) |
|
|
10
|
+
|------|---------------------|
|
|
11
|
+
| `claims.split` | sentence boundaries; `3.5 days` stays one claim; headings skipped; `?` lines skipped; `< 4` words skipped; `draft_line` correct |
|
|
12
|
+
| verdict mapping | checkable<0.5 → UNCHECKABLE; low confidence → REVIEW; supports/contradicts/not_found → PASS/FAIL/REVIEW at high confidence; threshold boundary inclusive |
|
|
13
|
+
| `report.render` | counts line correct; every claim appears once; bar length ∝ probability; empty verdicts list → still prints summary |
|
|
14
|
+
| log writer | one JSON line per run; round-trips `json.loads`; no `api_key` field anywhere |
|
|
15
|
+
|
|
16
|
+
Entry bar: `python -m tests.test_core` exits 0 on a clean tree. This is the one runnable check left behind with the code.
|
|
17
|
+
|
|
18
|
+
## 2. Live smoke (optional, real key)
|
|
19
|
+
|
|
20
|
+
- Fixture `examples/`: `source.md` (short policy: refunds in 3 days, card-and-bank only), `draft.md` (3 claims: one supported, one contradicted, one fluff).
|
|
21
|
+
- `sooth --source examples/source.md --text examples/draft.md`
|
|
22
|
+
- Pass when: process exits with code matching expected verdicts (1 FAIL present → exit 1), report table has 3 rows, `--log /tmp/t.jsonl` has exactly 1 line with `model` = pinned version.
|
|
23
|
+
- Script it as `tests/smoke.sh` (5 lines). Not run in unit CI. Skip entirely when `TYPESAFE_API_KEY` unset.
|
|
24
|
+
|
|
25
|
+
## 3. Accuracy calibration (live, labeled set)
|
|
26
|
+
|
|
27
|
+
Goal from PRD: ≥ 24/30 correct on a labeled set, zero confident-wrong `PASS` on contradicted claims.
|
|
28
|
+
|
|
29
|
+
- Labeled set: `examples/calibration.json` — 30 claims across `news-1.md`, `policy.md`, `pricing.md` (10 supported / 10 contradicted / 10 absent-or-opinion, each with an `expected` verdict and a note).
|
|
30
|
+
- Runner: `PYTHONPATH=src python3 tests/calibrate.py [--confidence T]` — batches live Jev calls, prints the confusion matrix, exits 0 only when the bar is met.
|
|
31
|
+
- Frozen matrix + notes: `examples/calibration.md` (threshold 0.7, `jev-1.13.0`, 30/30).
|
|
32
|
+
- Never move the threshold to hide a confident-wrong `PASS` — fix question wording instead (`instructions`/`criteria` in `verify.py`). Bump model version → re-run this layer.
|
|
33
|
+
|
|
34
|
+
## 4. Manual UX check (10 min)
|
|
35
|
+
|
|
36
|
+
- [ ] `sooth --help` alone explains enough to run.
|
|
37
|
+
- [ ] Report readable by someone who never saw the tool (show one person).
|
|
38
|
+
- [ ] Missing key → one-line hint mentioning `TYPESAFE_API_KEY`, exit 3, no stack trace.
|
|
39
|
+
- [ ] Exit codes: all-PASS fixture → 0; with-REVIEW fixture → 2; with-FAIL → 1.
|
|
40
|
+
|
|
41
|
+
## 5. Definition of done (v0.1)
|
|
42
|
+
|
|
43
|
+
1. `tests/test_core.py` green.
|
|
44
|
+
2. Smoke fixture behaves (§2) with a real key at least once.
|
|
45
|
+
3. Calibration table recorded (§3) and default threshold justified by it.
|
|
46
|
+
4. Manual checklist (§4) done.
|
|
47
|
+
5. Docs match reality: report column that shows probability distribution (not source snippets) — snippets deferred to v0.2 per `DESIGN.md`.
|
|
48
|
+
|
|
49
|
+
## Out of scope for testing now
|
|
50
|
+
|
|
51
|
+
Load/perf (CLI is one-shot), fuzzing, multi-language accuracy, regression CI on live API (costs money; add when thresholds ship to users).
|