lexara 0.1.0a1__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.
- lexara-0.1.0a1/.env.example +32 -0
- lexara-0.1.0a1/.gitignore +16 -0
- lexara-0.1.0a1/LICENSE.txt +16 -0
- lexara-0.1.0a1/PKG-INFO +485 -0
- lexara-0.1.0a1/README.md +435 -0
- lexara-0.1.0a1/demo/demo.py +152 -0
- lexara-0.1.0a1/demo/demo_script.md +164 -0
- lexara-0.1.0a1/eval/results/rewrite_eval_mock_2026-05-31.json +200 -0
- lexara-0.1.0a1/eval/results/rewrite_eval_openai_2026-05-31.json +200 -0
- lexara-0.1.0a1/eval/review/manual_review_mock_2026-05-31.md +18 -0
- lexara-0.1.0a1/eval/review/manual_review_openai_2026-05-31.md +18 -0
- lexara-0.1.0a1/examples/canonical/rewrite_already_at_target.json +136 -0
- lexara-0.1.0a1/examples/canonical/rewrite_hit_target.json +141 -0
- lexara-0.1.0a1/examples/canonical/rewrite_improved_miss.json +139 -0
- lexara-0.1.0a1/examples/demo_showcase.py +111 -0
- lexara-0.1.0a1/examples/sdk_rewrite_inspect_scores.py +44 -0
- lexara-0.1.0a1/examples/sdk_rewrite_to_target.py +34 -0
- lexara-0.1.0a1/examples/sdk_score_only.py +31 -0
- lexara-0.1.0a1/pyproject.toml +67 -0
- lexara-0.1.0a1/scripts/generate_review_table.py +164 -0
- lexara-0.1.0a1/scripts/pre_demo_check.sh +127 -0
- lexara-0.1.0a1/src/lexara/__init__.py +28 -0
- lexara-0.1.0a1/src/lexara/api/__init__.py +3 -0
- lexara-0.1.0a1/src/lexara/api/app.py +72 -0
- lexara-0.1.0a1/src/lexara/api/dependencies.py +51 -0
- lexara-0.1.0a1/src/lexara/api/errors.py +72 -0
- lexara-0.1.0a1/src/lexara/api/middleware.py +46 -0
- lexara-0.1.0a1/src/lexara/api/routes/__init__.py +0 -0
- lexara-0.1.0a1/src/lexara/api/routes/health.py +17 -0
- lexara-0.1.0a1/src/lexara/api/routes/readability.py +77 -0
- lexara-0.1.0a1/src/lexara/client.py +223 -0
- lexara-0.1.0a1/src/lexara/config.py +53 -0
- lexara-0.1.0a1/src/lexara/eval/__init__.py +14 -0
- lexara-0.1.0a1/src/lexara/eval/data/rewrite_effectiveness.json +123 -0
- lexara-0.1.0a1/src/lexara/eval/data/rewrite_fast.json +52 -0
- lexara-0.1.0a1/src/lexara/eval/harness.py +106 -0
- lexara-0.1.0a1/src/lexara/eval/models.py +137 -0
- lexara-0.1.0a1/src/lexara/eval/runner.py +91 -0
- lexara-0.1.0a1/src/lexara/exceptions.py +83 -0
- lexara-0.1.0a1/src/lexara/logging_config.py +54 -0
- lexara-0.1.0a1/src/lexara/models/__init__.py +38 -0
- lexara-0.1.0a1/src/lexara/models/common.py +27 -0
- lexara-0.1.0a1/src/lexara/models/errors.py +18 -0
- lexara-0.1.0a1/src/lexara/models/rewrite.py +162 -0
- lexara-0.1.0a1/src/lexara/models/scoring.py +87 -0
- lexara-0.1.0a1/src/lexara/options.py +28 -0
- lexara-0.1.0a1/src/lexara/rewriting/__init__.py +42 -0
- lexara-0.1.0a1/src/lexara/rewriting/pipeline.py +239 -0
- lexara-0.1.0a1/src/lexara/rewriting/prompts.py +111 -0
- lexara-0.1.0a1/src/lexara/rewriting/providers/__init__.py +32 -0
- lexara-0.1.0a1/src/lexara/rewriting/providers/base.py +55 -0
- lexara-0.1.0a1/src/lexara/rewriting/providers/errors.py +35 -0
- lexara-0.1.0a1/src/lexara/rewriting/providers/mock.py +128 -0
- lexara-0.1.0a1/src/lexara/rewriting/providers/openai.py +232 -0
- lexara-0.1.0a1/src/lexara/rewriting/providers/parsing.py +75 -0
- lexara-0.1.0a1/src/lexara/rewriting/providers/retry.py +42 -0
- lexara-0.1.0a1/src/lexara/rewriting/providers/usage.py +81 -0
- lexara-0.1.0a1/src/lexara/scoring/__init__.py +14 -0
- lexara-0.1.0a1/src/lexara/scoring/base.py +39 -0
- lexara-0.1.0a1/src/lexara/scoring/familiar_words.py +78 -0
- lexara-0.1.0a1/src/lexara/scoring/frameworks/__init__.py +21 -0
- lexara-0.1.0a1/src/lexara/scoring/frameworks/atos_estimated.py +33 -0
- lexara-0.1.0a1/src/lexara/scoring/frameworks/dale_chall.py +52 -0
- lexara-0.1.0a1/src/lexara/scoring/frameworks/flesch_kincaid.py +37 -0
- lexara-0.1.0a1/src/lexara/scoring/frameworks/lexile_estimated.py +72 -0
- lexara-0.1.0a1/src/lexara/scoring/interpret.py +66 -0
- lexara-0.1.0a1/src/lexara/scoring/registry.py +59 -0
- lexara-0.1.0a1/src/lexara/scoring/sentence_split.py +85 -0
- lexara-0.1.0a1/src/lexara/scoring/text_stats.py +74 -0
- lexara-0.1.0a1/src/lexara/services/__init__.py +13 -0
- lexara-0.1.0a1/src/lexara/services/rewrite_response.py +147 -0
- lexara-0.1.0a1/src/lexara/services/rewrite_service.py +55 -0
- lexara-0.1.0a1/src/lexara/services/scoring_service.py +87 -0
- lexara-0.1.0a1/tests/__init__.py +0 -0
- lexara-0.1.0a1/tests/conftest.py +29 -0
- lexara-0.1.0a1/tests/fixtures/scoring_calibration.json +80 -0
- lexara-0.1.0a1/tests/test_api_rewrite.py +122 -0
- lexara-0.1.0a1/tests/test_api_rewrite_workflow.py +42 -0
- lexara-0.1.0a1/tests/test_api_score.py +59 -0
- lexara-0.1.0a1/tests/test_api_score_edge_cases.py +15 -0
- lexara-0.1.0a1/tests/test_canonical_examples.py +33 -0
- lexara-0.1.0a1/tests/test_client.py +185 -0
- lexara-0.1.0a1/tests/test_eval_harness.py +183 -0
- lexara-0.1.0a1/tests/test_openai_contract.py +88 -0
- lexara-0.1.0a1/tests/test_openai_provider.py +192 -0
- lexara-0.1.0a1/tests/test_provider_retry.py +54 -0
- lexara-0.1.0a1/tests/test_rewrite_effectiveness.py +114 -0
- lexara-0.1.0a1/tests/test_rewrite_pipeline.py +63 -0
- lexara-0.1.0a1/tests/test_rewrite_pipeline_core.py +141 -0
- lexara-0.1.0a1/tests/test_rewrite_provider_failures.py +75 -0
- lexara-0.1.0a1/tests/test_rewrite_workflow_alpha.py +146 -0
- lexara-0.1.0a1/tests/test_scoring.py +89 -0
- lexara-0.1.0a1/tests/test_scoring_calibration.py +63 -0
- lexara-0.1.0a1/tests/test_scoring_formulas.py +74 -0
- lexara-0.1.0a1/tests/test_sentence_split.py +57 -0
- lexara-0.1.0a1/tests/test_text_stats.py +64 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Lexara API configuration
|
|
2
|
+
# Copy to `.env` and adjust. Loaded via pydantic-settings (prefix LEXARA_).
|
|
3
|
+
|
|
4
|
+
# Comma-separated list of valid API keys. Requests must send one of these as
|
|
5
|
+
# the `Authorization: Bearer <key>` header (or `x-api-key`).
|
|
6
|
+
LEXARA_API_KEYS=dev-local-key
|
|
7
|
+
|
|
8
|
+
# deployment: development | production
|
|
9
|
+
LEXARA_ENV=development
|
|
10
|
+
|
|
11
|
+
# LLM provider for the rewrite endpoint: "mock" | "openai"
|
|
12
|
+
#
|
|
13
|
+
# ALPHA NOTE: mock is for local dev/tests ONLY (word substitution, not an LLM).
|
|
14
|
+
# External demos and alpha customers require openai + LEXARA_OPENAI_API_KEY.
|
|
15
|
+
LEXARA_LLM_PROVIDER=mock
|
|
16
|
+
|
|
17
|
+
# Block mock when LEXARA_ENV=production (recommended for external alpha)
|
|
18
|
+
LEXARA_ALLOW_MOCK_PROVIDER=true
|
|
19
|
+
|
|
20
|
+
# Only needed when LEXARA_LLM_PROVIDER=openai
|
|
21
|
+
LEXARA_OPENAI_API_KEY=
|
|
22
|
+
LEXARA_OPENAI_MODEL=gpt-4o-mini
|
|
23
|
+
LEXARA_OPENAI_TIMEOUT_SECONDS=60
|
|
24
|
+
LEXARA_OPENAI_MAX_RETRIES=2
|
|
25
|
+
# Cap completion length per LLM call (cost + truncation safety).
|
|
26
|
+
LEXARA_OPENAI_MAX_COMPLETION_TOKENS=4096
|
|
27
|
+
|
|
28
|
+
# Logging: DEBUG | INFO | WARNING | ERROR
|
|
29
|
+
LEXARA_LOG_LEVEL=INFO
|
|
30
|
+
|
|
31
|
+
# Safety cap so a single request can't loop forever.
|
|
32
|
+
LEXARA_MAX_REWRITE_PASSES=5
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Copyright (c) 2026 Chanel Johnson
|
|
2
|
+
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary and confidential property of Chanel Johnson. Unauthorized copying,
|
|
7
|
+
distribution, modification, or use of this Software, in whole or in part, is
|
|
8
|
+
strictly prohibited without the express written permission of the copyright holder.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
11
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
12
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
13
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
14
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
15
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
16
|
+
SOFTWARE.
|
lexara-0.1.0a1/PKG-INFO
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lexara
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Reading level scoring and rewrite API for edtech
|
|
5
|
+
Project-URL: Homepage, https://github.com/ChanelYJ19/lexara
|
|
6
|
+
Project-URL: Documentation, https://github.com/ChanelYJ19/lexara#readme
|
|
7
|
+
Author-email: Chanel Johnson <chanel_johnson@alumni.brown.edu>
|
|
8
|
+
License: Copyright (c) 2026 Chanel Johnson
|
|
9
|
+
|
|
10
|
+
All rights reserved.
|
|
11
|
+
|
|
12
|
+
This software and associated documentation files (the "Software") are the
|
|
13
|
+
proprietary and confidential property of Chanel Johnson. Unauthorized copying,
|
|
14
|
+
distribution, modification, or use of this Software, in whole or in part, is
|
|
15
|
+
strictly prohibited without the express written permission of the copyright holder.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
24
|
+
License-File: LICENSE.txt
|
|
25
|
+
Keywords: ai,edtech,education,nlp,readability
|
|
26
|
+
Classifier: Development Status :: 3 - Alpha
|
|
27
|
+
Classifier: Intended Audience :: Developers
|
|
28
|
+
Classifier: Programming Language :: Python :: 3
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
34
|
+
Classifier: Topic :: Education
|
|
35
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
36
|
+
Requires-Python: >=3.9
|
|
37
|
+
Requires-Dist: httpx>=0.24
|
|
38
|
+
Requires-Dist: pydantic>=2.0
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: httpx>=0.24; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
43
|
+
Provides-Extra: openai
|
|
44
|
+
Requires-Dist: openai>=1.30; extra == 'openai'
|
|
45
|
+
Provides-Extra: server
|
|
46
|
+
Requires-Dist: fastapi>=0.110; extra == 'server'
|
|
47
|
+
Requires-Dist: pydantic-settings>=2.2; extra == 'server'
|
|
48
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'server'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# Lexara
|
|
52
|
+
|
|
53
|
+
**Rewrite educational text to a target grade level. Return multi-framework proof of what changed.**
|
|
54
|
+
|
|
55
|
+
Lexara is developer infrastructure for edtech builders. One API call scores a passage, rewrites it toward a target US grade level using OpenAI (gpt-4o-mini), rescores after each pass, and returns `input` → `output` with per-framework before/after proof.
|
|
56
|
+
|
|
57
|
+
**Measured against a 12-passage K-12 dataset (science, social studies, math, ELA, grades 2–12):**
|
|
58
|
+
- **58% of passages hit the target grade** within ±1 grade level (7/12 with OpenAI gpt-4o-mini)
|
|
59
|
+
- **Average grade reduction: −8.9 levels** per rewrite
|
|
60
|
+
- **Works best for grade 4–12 targets.** Passages targeting below grade 4 on academic-register text consistently fall short — see [Accuracy & Limitations](#accuracy--limitations).
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
curl -s http://localhost:8000/v1/readability/rewrite \
|
|
64
|
+
-H "Authorization: Bearer dev-local-key" \
|
|
65
|
+
-H "Content-Type: application/json" \
|
|
66
|
+
-d '{"text":"Photosynthesis is the biochemical process by which chlorophyll-containing organisms convert light energy into chemical energy.","target_grade":6,"max_passes":5}'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Not a readability score you stare at. A rewrite you can act on — with scored evidence before and after.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Why Lexara exists
|
|
74
|
+
|
|
75
|
+
### Score-only is not enough
|
|
76
|
+
|
|
77
|
+
A score-only API tells you a worksheet reads at grade 11. It does not give you a grade-5 version. Your user still has to rewrite by hand, guess whether it worked, and hope one readability number reflects their district's expectations.
|
|
78
|
+
|
|
79
|
+
That is a diagnostic. Edtech products need an **action**: adjust the text, verify the result, retry if needed.
|
|
80
|
+
|
|
81
|
+
### Why multi-framework scoring + rewrite together
|
|
82
|
+
|
|
83
|
+
No single readability formula is authoritative — districts, publishers, and literacy tools reference different frameworks (Flesch-Kincaid, Dale-Chall, Lexile-style, ATOS-style). Lexara:
|
|
84
|
+
|
|
85
|
+
1. **Scores across frameworks** before and after the rewrite
|
|
86
|
+
2. **Rewrites toward your target grade**, not a single opaque number
|
|
87
|
+
3. **Returns proof** — `outcome.hit_target`, `frameworks_improved`, and per-framework grades on both sides
|
|
88
|
+
|
|
89
|
+
Scoring is the **verification layer** for the rewrite. The product is the loop: **rewrite → rescore → prove**.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Quick start
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
97
|
+
pip install -e ".[dev]"
|
|
98
|
+
cp .env.example .env
|
|
99
|
+
lexara-api # → http://localhost:8000/docs
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> **Alpha note:** Default `LEXARA_LLM_PROVIDER=mock` is for **local dev and tests only**. It uses word substitution, not an LLM — expect low `hit_target` on hard passages. **External demos and alpha require `openai`.** Run `python examples/demo_showcase.py` to see a mock warning banner when the API uses mock.
|
|
103
|
+
|
|
104
|
+
### 1. Rewrite (curl) — start here
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
curl -s http://localhost:8000/v1/readability/rewrite \
|
|
108
|
+
-H "Authorization: Bearer dev-local-key" \
|
|
109
|
+
-H "Content-Type: application/json" \
|
|
110
|
+
-d '{
|
|
111
|
+
"text": "Photosynthesis is the biochemical process by which chlorophyll-containing organisms convert light energy into chemical energy, subsequently producing glucose and releasing oxygen as a byproduct of cellular metabolism.",
|
|
112
|
+
"target_grade": 6,
|
|
113
|
+
"preserve_meaning": true,
|
|
114
|
+
"max_passes": 5
|
|
115
|
+
}' | python3 -m json.tool
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Actual output** (OpenAI gpt-4o-mini, measured):
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
input.estimated_grade_level: 18.3
|
|
122
|
+
output.estimated_grade_level: 7.4
|
|
123
|
+
outcome.hit_target: true
|
|
124
|
+
output.text: "Photosynthesis helps plants make food. Plants have a green color
|
|
125
|
+
called chlorophyll. This color captures light from the sun. Then,
|
|
126
|
+
plants change this light into chemical energy to produce glucose,
|
|
127
|
+
and they release oxygen as a result."
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Look for: `outcome.summary`, `outcome.hit_target`, `input.estimated_grade_level`, `output.estimated_grade_level`, `output.text`.
|
|
131
|
+
|
|
132
|
+
### 2. Rewrite (Python SDK)
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from lexara import LexaraClient, Tone
|
|
136
|
+
|
|
137
|
+
client = LexaraClient(api_key="dev-local-key")
|
|
138
|
+
|
|
139
|
+
result = client.readability.rewrite(
|
|
140
|
+
"Photosynthesis is the biochemical process by which chlorophyll-containing "
|
|
141
|
+
"organisms convert light energy into chemical energy.",
|
|
142
|
+
target_grade=6,
|
|
143
|
+
preserve_meaning=True,
|
|
144
|
+
max_passes=5,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
print(result.outcome.summary)
|
|
148
|
+
print(f"{result.input.estimated_grade_level} → {result.output.estimated_grade_level} (target {result.target.grade})")
|
|
149
|
+
print(f"Hit target: {result.hit_target} | Frameworks improved: {result.outcome.frameworks_improved}")
|
|
150
|
+
print(result.output.text)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 3. Score only (when you don't need a rewrite yet)
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
scored = client.readability.score(
|
|
157
|
+
passage,
|
|
158
|
+
frameworks=["flesch_kincaid", "lexile"],
|
|
159
|
+
)
|
|
160
|
+
print(scored.aggregate.estimated_grade_level)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Use `POST /v1/readability/score` for diagnostics. Use `rewrite` when you need to **act** on the result.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Demo-ready examples
|
|
168
|
+
|
|
169
|
+
Three passages you can run in a customer call, Show HN thread, or live demo.
|
|
170
|
+
|
|
171
|
+
> **Mock provider:** Demos against local `lexara-api` with default settings use mock. Rewrite quality is not production-representative. For customer-facing demos set `LEXARA_LLM_PROVIDER=openai`.
|
|
172
|
+
|
|
173
|
+
### Demo A — Science passage: college → grade 6
|
|
174
|
+
|
|
175
|
+
**Story:** *"Your curriculum import is too hard for middle school. One call fixes and proves it."*
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
curl -s http://localhost:8000/v1/readability/rewrite \
|
|
179
|
+
-H "Authorization: Bearer dev-local-key" \
|
|
180
|
+
-H "Content-Type: application/json" \
|
|
181
|
+
-d '{
|
|
182
|
+
"text": "Photosynthesis is the biochemical process by which chlorophyll-containing organisms convert light energy into chemical energy, subsequently producing glucose and releasing oxygen as a byproduct of cellular metabolism.",
|
|
183
|
+
"target_grade": 6,
|
|
184
|
+
"max_passes": 5,
|
|
185
|
+
"tolerance": 1.5
|
|
186
|
+
}'
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from lexara import LexaraClient
|
|
191
|
+
|
|
192
|
+
PASSAGE = (
|
|
193
|
+
"Photosynthesis is the biochemical process by which chlorophyll-containing "
|
|
194
|
+
"organisms convert light energy into chemical energy, subsequently producing "
|
|
195
|
+
"glucose and releasing oxygen as a byproduct of cellular metabolism."
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
client = LexaraClient(api_key="dev-local-key")
|
|
199
|
+
r = client.readability.rewrite(PASSAGE, target_grade=6, max_passes=5, tolerance=1.5)
|
|
200
|
+
print(f"Before: grade {r.input.estimated_grade_level} After: grade {r.output.estimated_grade_level}")
|
|
201
|
+
print(r.output.text)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Talk track:** Show `input.frameworks[]` vs `output.frameworks[]` — four frameworks moved, not one proprietary number.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### Demo B — Worksheet instructions: teacher tone, grade 4
|
|
209
|
+
|
|
210
|
+
**Story:** *"Teachers paste LMS instructions. Lexara returns student-ready text with meaning preserved."*
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from lexara import LexaraClient, Tone
|
|
214
|
+
|
|
215
|
+
client = LexaraClient(api_key="dev-local-key")
|
|
216
|
+
|
|
217
|
+
result = client.readability.rewrite(
|
|
218
|
+
"Students will subsequently utilize the provided manipulatives to demonstrate "
|
|
219
|
+
"their comprehension of fractional equivalence, and they must obtain sufficient "
|
|
220
|
+
"evidence before completing the assessment.",
|
|
221
|
+
target_grade=4,
|
|
222
|
+
tone=Tone.friendly,
|
|
223
|
+
preserve_meaning=True,
|
|
224
|
+
max_passes=4,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
if result.hit_target:
|
|
228
|
+
ship_to_lms(result.output.text)
|
|
229
|
+
else:
|
|
230
|
+
print(result.outcome.summary, result.execution.warnings)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Run: `python examples/sdk_rewrite_to_target.py`
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### Demo C — Already at target: no LLM call
|
|
238
|
+
|
|
239
|
+
**Story:** *"Lexara doesn't burn tokens when content is already right — it scores, verifies, and skips."*
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from lexara import LexaraClient
|
|
243
|
+
|
|
244
|
+
client = LexaraClient(api_key="dev-local-key")
|
|
245
|
+
|
|
246
|
+
result = client.readability.rewrite(
|
|
247
|
+
"Photosynthesis lets plants make food from sunlight. Plants need sun and water to grow.",
|
|
248
|
+
target_grade=5,
|
|
249
|
+
tolerance=1.0,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
assert result.execution.skipped is True
|
|
253
|
+
assert result.execution.passes_used == 0
|
|
254
|
+
assert result.hit_target is True
|
|
255
|
+
assert result.output.text == result.input.text
|
|
256
|
+
print(result.outcome.summary)
|
|
257
|
+
# → "Already at grade 5.1 (target 5, within ±1). No rewrite needed."
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Full payload: `examples/canonical/rewrite_already_at_target.json`
|
|
261
|
+
|
|
262
|
+
**Run all three:** `python examples/demo_showcase.py`
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## API
|
|
267
|
+
|
|
268
|
+
| Method | Path | Purpose |
|
|
269
|
+
|--------|------|---------|
|
|
270
|
+
| `POST` | `/v1/readability/rewrite` | **Primary** — rewrite to target grade + multi-framework proof |
|
|
271
|
+
| `POST` | `/v1/readability/score` | Diagnostics only — no rewrite |
|
|
272
|
+
| `GET` | `/health` | Liveness |
|
|
273
|
+
|
|
274
|
+
Auth: `Authorization: Bearer <key>` or `x-api-key: <key>`
|
|
275
|
+
|
|
276
|
+
### Rewrite response (read in this order)
|
|
277
|
+
|
|
278
|
+
| Block | Meaning |
|
|
279
|
+
|-------|---------|
|
|
280
|
+
| `input` / `output` | Original vs rewritten text + per-framework grades |
|
|
281
|
+
| `outcome` | `hit_target`, grade from → to, `frameworks_improved`, `summary` |
|
|
282
|
+
| `target` | Goal grade, tolerance, grade band |
|
|
283
|
+
| `execution` | Passes used, provider, `skipped`, `warnings` |
|
|
284
|
+
|
|
285
|
+
Shortcuts: `rewritten_text`, `hit_target`.
|
|
286
|
+
|
|
287
|
+
Canonical JSON: [`examples/canonical/`](examples/canonical/)
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Python SDK
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
from lexara import LexaraClient, Tone, RewriteOptions
|
|
295
|
+
|
|
296
|
+
client = LexaraClient(api_key="...", timeout=120, max_retries=2)
|
|
297
|
+
|
|
298
|
+
# Hero workflow
|
|
299
|
+
result = client.readability.rewrite(
|
|
300
|
+
text,
|
|
301
|
+
target_grade=5,
|
|
302
|
+
preserve_meaning=True,
|
|
303
|
+
tone=Tone.friendly,
|
|
304
|
+
max_passes=4,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# Or bundle options (all fields optional except target_grade on rewrite call)
|
|
308
|
+
opts = RewriteOptions(preserve_meaning=True, tone=Tone.friendly, max_passes=4)
|
|
309
|
+
result = client.readability.rewrite(text, target_grade=5, options=opts)
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
| Parameter | Default | Meaning |
|
|
313
|
+
|-----------|---------|---------|
|
|
314
|
+
| `target_grade` | required | Desired US grade level (1–16) |
|
|
315
|
+
| `preserve_meaning` | `True` | Keep facts, numbers, names, steps |
|
|
316
|
+
| `tone` | `neutral` | `friendly`, `formal`, `playful`, `academic`, … |
|
|
317
|
+
| `max_passes` | `3` | Max rewrite → rescore iterations |
|
|
318
|
+
| `frameworks` | all | Frameworks used to judge progress |
|
|
319
|
+
| `tolerance` | `1.0` | Grade levels within target = hit |
|
|
320
|
+
|
|
321
|
+
**Examples:** `examples/sdk_rewrite_to_target.py` · `examples/sdk_rewrite_inspect_scores.py` · `examples/sdk_score_only.py`
|
|
322
|
+
|
|
323
|
+
**Exceptions:** `AuthenticationError` · `ValidationError` · `LexaraTimeoutError` · `LexaraConnectionError` · `LexaraAPIError`
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## How rewrite works
|
|
328
|
+
|
|
329
|
+
Each `POST /v1/readability/rewrite` request:
|
|
330
|
+
|
|
331
|
+
1. Scores `input` across selected frameworks
|
|
332
|
+
2. Skips the LLM if already within `tolerance` of `target_grade`
|
|
333
|
+
3. Otherwise **rewrite → rescore → retry** up to `max_passes`
|
|
334
|
+
4. Returns the best attempt with full before/after snapshots
|
|
335
|
+
|
|
336
|
+
Provider failures become `execution.warnings` — the endpoint returns 200 with the best text so far. Check `outcome.hit_target` before shipping to users.
|
|
337
|
+
|
|
338
|
+
`execution.degraded` means **no usable rewrite was produced** (e.g. all provider calls failed). Warnings alone do not set `degraded` when a rewrite succeeded.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Frameworks & safe claims
|
|
343
|
+
|
|
344
|
+
| Framework | ID | Confidence | What to tell customers |
|
|
345
|
+
|-----------|-----|------------|------------------------|
|
|
346
|
+
| Flesch-Kincaid Grade | `flesch_kincaid` | **exact** | Standard formula; syllable counts use an estimated syllable heuristic |
|
|
347
|
+
| New Dale-Chall | `dale_chall` | **estimated** | Published formula; familiar-word list is approximate, not the licensed 3,000-word list |
|
|
348
|
+
| ATOS-style | `atos_estimated` | **estimated** | ATOS-style estimate — **not** an official Accelerated Reader level |
|
|
349
|
+
| Lexile-equivalent | `lexile_estimated` | **estimated** | Lexile-scale estimate — **not** an official MetaMetrics Lexile score |
|
|
350
|
+
|
|
351
|
+
Legacy aliases: `atos` → `atos_estimated`, `lexile` → `lexile_estimated`.
|
|
352
|
+
|
|
353
|
+
### Safe to say
|
|
354
|
+
|
|
355
|
+
- Rewrite toward a target US grade level in one API call
|
|
356
|
+
- Multi-framework before/after proof (`input.frameworks` → `output.frameworks`)
|
|
357
|
+
- Flesch-Kincaid uses the standard grade formula; other frameworks are clearly labeled `estimated`
|
|
358
|
+
- Developer-friendly SDK: `client.readability.rewrite(text, target_grade=6)`
|
|
359
|
+
|
|
360
|
+
### Do not say
|
|
361
|
+
|
|
362
|
+
- Official Lexile, ATOS, or Dale-Chall certification
|
|
363
|
+
- Guaranteed rewrite success on every passage — always check `outcome.hit_target`
|
|
364
|
+
- Single "true" reading level — Lexara returns multiple frameworks intentionally
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Accuracy & Limitations
|
|
369
|
+
|
|
370
|
+
Measured with OpenAI gpt-4o-mini against a 12-passage K-12 dataset (v2, May 2026):
|
|
371
|
+
|
|
372
|
+
- **Below grade 4 on academic-register text: expect misses.** A passage scoring 17.1 targeting grade 2 landed at 4.5 after five passes — still 2.5 levels off. The pipeline simplifies vocabulary and sentence structure, but readability formulas score short function words and sentence count, not whether the content is truly accessible to a 7-year-old.
|
|
373
|
+
- **Borderline passages vary run to run.** A grade-4 target passage hit 4.5 in one run and 7.0 in the next with the same prompt and model. Always check `outcome.hit_target` at call time; do not cache a one-time pass as a permanent quality signal.
|
|
374
|
+
- **Extreme grade gaps (> 12 levels) reliably miss — this is expected.** The economics stress-test passage (original grade 22.3 → target 5.0) landed at 9.8. A single rewrite loop cannot close a 17-level gap; this failure mode is intentional and documented in the dataset.
|
|
375
|
+
- **Human review is recommended before shipping to students.** `outcome.hit_target` confirms the scored grade landed in range — it does not verify that meaning was preserved, that the text is age-appropriate in tone, or that domain-specific terms were handled correctly. Treat the output as a draft, not a final.
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Configuration
|
|
380
|
+
|
|
381
|
+
See `.env.example`.
|
|
382
|
+
|
|
383
|
+
| Var | Default | Meaning |
|
|
384
|
+
|-----|---------|---------|
|
|
385
|
+
| `LEXARA_ENV` | `development` | `development` or `production` |
|
|
386
|
+
| `LEXARA_ALLOW_MOCK_PROVIDER` | `true` | Set `false` with `LEXARA_ENV=production` to block mock |
|
|
387
|
+
| `LEXARA_LLM_PROVIDER` | `mock` | `mock` (dev/test) or `openai` (external alpha) |
|
|
388
|
+
| `LEXARA_OPENAI_API_KEY` | – | Required when provider is `openai` |
|
|
389
|
+
| `LEXARA_OPENAI_MODEL` | `gpt-4o-mini` | Chat model for rewrites |
|
|
390
|
+
| `LEXARA_API_KEYS` | `dev-local-key` | Valid API keys |
|
|
391
|
+
|
|
392
|
+
Real provider setup:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
pip install -e ".[openai]"
|
|
396
|
+
export LEXARA_LLM_PROVIDER=openai
|
|
397
|
+
export LEXARA_OPENAI_API_KEY=sk-...
|
|
398
|
+
lexara-api
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Rewrite effectiveness evals
|
|
404
|
+
|
|
405
|
+
Measure rewrite quality before demos — not just formula correctness:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
lexara-eval # table summary (mock)
|
|
409
|
+
lexara-eval --output eval/results.json # save JSON for review
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Dataset: `src/lexara/eval/data/rewrite_effectiveness.json` (grades 2–12, five passage types).
|
|
413
|
+
|
|
414
|
+
With OpenAI: `lexara-eval --provider openai --output eval/results-openai.json`
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Homepage & API copy (suggested)
|
|
419
|
+
|
|
420
|
+
**Headline:** Rewrite text to your target grade. Prove it across four frameworks.
|
|
421
|
+
|
|
422
|
+
**Subhead:** Lexara is edtech API infrastructure — score, rewrite, rescore, and return before/after proof in one call. Not a number. A number you acted on.
|
|
423
|
+
|
|
424
|
+
**API one-liner:** `POST /v1/readability/rewrite` — rewrite toward a target grade; get `input`, `output`, and `outcome.hit_target` with Flesch-Kincaid, Dale-Chall, ATOS-style, and Lexile-equivalent verification.
|
|
425
|
+
|
|
426
|
+
**Bullets for landing page:**
|
|
427
|
+
- Score-only APIs diagnose. Lexara rewrites and proves the result.
|
|
428
|
+
- Multi-framework before/after — not one proprietary readability number.
|
|
429
|
+
- One SDK call: `client.readability.rewrite(passage, target_grade=6)`
|
|
430
|
+
- Skips the LLM when text is already at target.
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Demo script (5 minutes)
|
|
435
|
+
|
|
436
|
+
Use this flow in a customer call or Show HN live demo:
|
|
437
|
+
|
|
438
|
+
1. **Problem (30s)** — Paste the Demo A science passage. *"This reads at college level. Your middle-school product can't use it as-is."*
|
|
439
|
+
2. **Rewrite (60s)** — Run curl or SDK. Show `outcome.summary` and grade drop in `input` → `output`.
|
|
440
|
+
3. **Proof (60s)** — Expand `input.frameworks` and `output.frameworks`. *"We don't trust one formula — we show four, before and after."*
|
|
441
|
+
4. **Teacher workflow (60s)** — Demo B worksheet instructions with `tone=friendly`. Show rewritten bullet-friendly text.
|
|
442
|
+
5. **Efficiency (30s)** — Demo C already-at-target. *"No LLM call when content is already right."*
|
|
443
|
+
6. **Ship check (30s)** — Point at `outcome.hit_target`, `execution.warnings`, and the safe-claims table. *"You decide when to ship; Lexara gives you structured proof."*
|
|
444
|
+
|
|
445
|
+
Runnable: `python examples/demo_showcase.py`
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## Alpha release checklist
|
|
450
|
+
|
|
451
|
+
Before any external demo or alpha customer:
|
|
452
|
+
|
|
453
|
+
- [ ] `LEXARA_LLM_PROVIDER=openai` and `LEXARA_OPENAI_API_KEY` set
|
|
454
|
+
- [ ] `LEXARA_ENV=production` and `LEXARA_ALLOW_MOCK_PROVIDER=false` (optional guard)
|
|
455
|
+
- [ ] `lexara-eval --provider openai --output eval/results-openai.json` — review `hit_target` rate
|
|
456
|
+
- [ ] Manual semantic review on Demo A/B/C (`python examples/demo_showcase.py`)
|
|
457
|
+
- [ ] `pytest -m integration` with live OpenAI key
|
|
458
|
+
- [ ] Safe-claims table reviewed in customer-facing materials
|
|
459
|
+
- [ ] Confirm `execution.degraded` vs `execution.warnings` behavior with integrators
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Architecture
|
|
464
|
+
|
|
465
|
+
| Layer | Role |
|
|
466
|
+
|-------|------|
|
|
467
|
+
| `api/` | HTTP, auth, middleware |
|
|
468
|
+
| `services/` | Rewrite + scoring orchestration |
|
|
469
|
+
| `scoring/` | Pure readability formulas |
|
|
470
|
+
| `rewriting/` | LLM provider + rewrite/rescore pipeline |
|
|
471
|
+
| `eval/` | Rewrite effectiveness harness |
|
|
472
|
+
| `client.py` | Python SDK |
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Tests
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
pytest # full suite (mock, no network)
|
|
480
|
+
pytest tests/test_eval_harness.py -v
|
|
481
|
+
pytest tests/test_client.py -v
|
|
482
|
+
|
|
483
|
+
# Live OpenAI — opt-in:
|
|
484
|
+
LEXARA_OPENAI_API_KEY=sk-... pytest -m integration -v
|
|
485
|
+
```
|