prettyplay 0.0.2__py3-none-any.whl
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.
- prettyplay/.usages/lifecycle.md +117 -0
- prettyplay/.usages/steps.md +169 -0
- prettyplay/__init__.py +18 -0
- prettyplay/cache/.usages/addressing.md +44 -0
- prettyplay/cache/.usages/budgets.md +51 -0
- prettyplay/cache/.usages/storage.md +32 -0
- prettyplay/cache/__init__.py +8 -0
- prettyplay/cache/budgets.py +93 -0
- prettyplay/cache/models.py +55 -0
- prettyplay/cache/store.py +276 -0
- prettyplay/cache/text.py +36 -0
- prettyplay/config/.usages/configuration.md +246 -0
- prettyplay/config/__init__.py +6 -0
- prettyplay/config/loader.py +336 -0
- prettyplay/config/models.py +288 -0
- prettyplay/driver/.usages/error_kinds.md +26 -0
- prettyplay/driver/.usages/plumbing.md +52 -0
- prettyplay/driver/__init__.py +11 -0
- prettyplay/driver/errors.py +76 -0
- prettyplay/driver/page.py +175 -0
- prettyplay/driver/session.py +330 -0
- prettyplay/engine/.usages/generation.md +157 -0
- prettyplay/engine/.usages/healing.md +61 -0
- prettyplay/engine/__init__.py +19 -0
- prettyplay/engine/attempts.py +80 -0
- prettyplay/engine/classification.py +70 -0
- prettyplay/engine/compliance.py +125 -0
- prettyplay/engine/execution.py +48 -0
- prettyplay/engine/generator.py +1110 -0
- prettyplay/engine/groups/.usages/recovery.md +71 -0
- prettyplay/engine/groups/__init__.py +7 -0
- prettyplay/engine/groups/diagnosis.py +128 -0
- prettyplay/engine/groups/outcome.py +55 -0
- prettyplay/engine/groups/recovery.py +491 -0
- prettyplay/engine/healer.py +149 -0
- prettyplay/engine/polling/.usages/settle.md +55 -0
- prettyplay/engine/polling/__init__.py +6 -0
- prettyplay/engine/polling/settle.py +98 -0
- prettyplay/engine/polling/window.py +72 -0
- prettyplay/engine/renderer/.usages/memory.md +24 -0
- prettyplay/engine/renderer/.usages/rendering.md +44 -0
- prettyplay/engine/renderer/.usages/validation.md +20 -0
- prettyplay/engine/renderer/__init__.py +7 -0
- prettyplay/engine/renderer/memory.py +37 -0
- prettyplay/engine/renderer/render.py +184 -0
- prettyplay/engine/renderer/validation.py +76 -0
- prettyplay/engine/steering/.usages/steering.md +95 -0
- prettyplay/engine/steering/__init__.py +5 -0
- prettyplay/engine/steering/steering.py +765 -0
- prettyplay/engine/text.py +41 -0
- prettyplay/executor.py +654 -0
- prettyplay/failures/.usages/taxonomy.md +131 -0
- prettyplay/failures/__init__.py +25 -0
- prettyplay/failures/errors.py +359 -0
- prettyplay/groups.py +276 -0
- prettyplay/llm/.usages/classification.md +31 -0
- prettyplay/llm/.usages/providers.md +127 -0
- prettyplay/llm/.usages/transport.md +81 -0
- prettyplay/llm/__init__.py +38 -0
- prettyplay/llm/_request.py +823 -0
- prettyplay/llm/anthropic_provider.py +468 -0
- prettyplay/llm/models.py +273 -0
- prettyplay/llm/openai_provider.py +444 -0
- prettyplay/llm/provider.py +279 -0
- prettyplay/reporting/.usages/hooks.md +61 -0
- prettyplay/reporting/__init__.py +6 -0
- prettyplay/reporting/hooks.py +47 -0
- prettyplay/reporting/reporter.py +70 -0
- prettyplay/runtime.py +81 -0
- prettyplay/scenario.py +469 -0
- prettyplay-0.0.2.dist-info/METADATA +515 -0
- prettyplay-0.0.2.dist-info/RECORD +75 -0
- prettyplay-0.0.2.dist-info/WHEEL +5 -0
- prettyplay-0.0.2.dist-info/licenses/LICENSE +28 -0
- prettyplay-0.0.2.dist-info/top_level.txt +1 -0
prettyplay/llm/models.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Models of the prettyplay.llm cell: the classification verdicts, the compliance findings and the scenario records."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from json_repair import loads as repair_loads
|
|
6
|
+
from pydantic import BaseModel, ConfigDict
|
|
7
|
+
|
|
8
|
+
from ..failures import ComplianceVerdictError
|
|
9
|
+
|
|
10
|
+
#: The frozen set of the three compliance priority labels.
|
|
11
|
+
COMPLIANCE_PRIORITIES = frozenset({"high", "medium", "low"})
|
|
12
|
+
|
|
13
|
+
#: The frozen set of the two compliance dimension labels.
|
|
14
|
+
COMPLIANCE_DIMENSIONS = frozenset({"instruction", "adequacy"})
|
|
15
|
+
|
|
16
|
+
#: The frozen set of the three group diagnosis category labels.
|
|
17
|
+
GROUP_CATEGORIES = frozenset({"recoverable", "product_defect", "incurable"})
|
|
18
|
+
|
|
19
|
+
#: The recommendation a degraded group diagnosis answer carries.
|
|
20
|
+
DEGRADED_GROUP_RECOMMENDATION = "re-run the group step or check the provider answer"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class FailureClassification(BaseModel):
|
|
24
|
+
"""The verdict of a failure classification.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
category: the classification label: rot, product_defect, fixable or
|
|
28
|
+
incurable — fixable: the step code is at fault (an ambiguous or
|
|
29
|
+
wrong locator or strategy) while the intent stays satisfiable;
|
|
30
|
+
regeneration for the same intent can help.
|
|
31
|
+
explanation: why the failure got this category.
|
|
32
|
+
recommendation: the recommended engineer action.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
model_config = ConfigDict(kw_only=True)
|
|
36
|
+
|
|
37
|
+
category: str
|
|
38
|
+
explanation: str
|
|
39
|
+
recommendation: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ComplianceFinding(BaseModel):
|
|
43
|
+
"""One finding of the instruction compliance verdict.
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
instruction: the verbatim quote the finding names — the violated
|
|
47
|
+
instruction or the fragment of the step sentence the code
|
|
48
|
+
fails to accomplish.
|
|
49
|
+
priority: the finding priority: high, medium or low; only high
|
|
50
|
+
blocks the candidate, in both dimensions.
|
|
51
|
+
explanation: one short sentence why the code violates the
|
|
52
|
+
instruction or fails the step.
|
|
53
|
+
dimension: the finding dimension: instruction or adequacy —
|
|
54
|
+
which side of the gate produced the finding.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
model_config = ConfigDict(kw_only=True)
|
|
58
|
+
|
|
59
|
+
instruction: str = ""
|
|
60
|
+
priority: str = ""
|
|
61
|
+
explanation: str = ""
|
|
62
|
+
dimension: str = ""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class GroupFailureClassification(BaseModel):
|
|
66
|
+
"""The verdict of a group diagnosis.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
category: the diagnosis label: recoverable (the affected steps of
|
|
70
|
+
the group can be regenerated and the run fixed), product_defect
|
|
71
|
+
(the application is genuinely broken — never healed green),
|
|
72
|
+
incurable (the root cannot be reached from inside the group, or
|
|
73
|
+
regeneration cannot help); a degraded answer carries the
|
|
74
|
+
conservative incurable.
|
|
75
|
+
root_cause: why the failure got this category; a degraded verdict
|
|
76
|
+
carries the raw answer here.
|
|
77
|
+
earliest_step: the verbatim sentence quote of the earliest affected
|
|
78
|
+
step as answered.
|
|
79
|
+
recommendation: the recommended regeneration or engineer action.
|
|
80
|
+
degraded: whether the parse degraded the raw answer to the
|
|
81
|
+
conservative incurable; False on every parsed answer, True only
|
|
82
|
+
on the degradation path — a library-set flag, never part of the
|
|
83
|
+
model answer; the calling engine logs its WARNING from this flag.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
model_config = ConfigDict(kw_only=True)
|
|
87
|
+
|
|
88
|
+
category: str
|
|
89
|
+
root_cause: str
|
|
90
|
+
earliest_step: str
|
|
91
|
+
recommendation: str
|
|
92
|
+
degraded: bool = False
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ScenarioStep(BaseModel):
|
|
96
|
+
"""One record of the scenario context of a test — the unit every PREVIOUS STEPS block renders.
|
|
97
|
+
|
|
98
|
+
Attributes:
|
|
99
|
+
sentence: the raw step sentence (the template source) as written by
|
|
100
|
+
the engineer, verbatim — the addressing and diagnostics
|
|
101
|
+
artifact; never rendered into requests.
|
|
102
|
+
instruction: the prepared instruction recorded at the step's
|
|
103
|
+
execution — the rendered plain-text sentence; equals
|
|
104
|
+
``sentence`` for a non-template step; the only field requests
|
|
105
|
+
render.
|
|
106
|
+
group_prompt: the group prompt of the step's group; empty — an
|
|
107
|
+
ordinary step; non-empty — the verbatim group prompt; the
|
|
108
|
+
membership is permanent for the lifetime of the test context.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
model_config = ConfigDict(kw_only=True, frozen=True)
|
|
112
|
+
|
|
113
|
+
sentence: str = ""
|
|
114
|
+
instruction: str = ""
|
|
115
|
+
group_prompt: str = ""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _answer_fragment(verdict_text: str) -> str:
|
|
119
|
+
"""Collapse the raw verdict answer to a diagnostic fragment.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
verdict_text: the raw text answer of the verdict model.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
The whitespace-collapsed first 200 characters of the raw text.
|
|
126
|
+
"""
|
|
127
|
+
return " ".join(verdict_text.split())[:200]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _malformed(verdict_text: str) -> ComplianceVerdictError:
|
|
131
|
+
"""Build the loud failure of a malformed verdict answer.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
verdict_text: the raw text answer that did not parse.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
The ComplianceVerdictError carrying a fragment of the raw answer.
|
|
138
|
+
"""
|
|
139
|
+
return ComplianceVerdictError(
|
|
140
|
+
"compliance verdict unparsable — expected a JSON list of findings with "
|
|
141
|
+
"instruction, priority high|medium|low, explanation and dimension instruction|adequacy; "
|
|
142
|
+
"received fragment: " + _answer_fragment(verdict_text)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def parse_compliance_verdict(verdict_text: str) -> list[ComplianceFinding]:
|
|
147
|
+
"""Parse the raw answer of the compliance verdict request into findings.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
verdict_text: the raw text answer of the verdict model;
|
|
151
|
+
whitespace-padded JSON is tolerated.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
The parsed findings; an empty list means compliant — only from an
|
|
155
|
+
explicitly valid JSON ``[]``; a syntax failure is salvaged once
|
|
156
|
+
through ``json_repair``.
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
ComplianceVerdictError: the answer resolves to neither the required
|
|
160
|
+
shape nor a repairable equivalent — raised loudly, never waved
|
|
161
|
+
through; the candidate stays unchecked and is never cached.
|
|
162
|
+
"""
|
|
163
|
+
text = verdict_text.strip()
|
|
164
|
+
try:
|
|
165
|
+
data = json.loads(text)
|
|
166
|
+
except ValueError:
|
|
167
|
+
try:
|
|
168
|
+
data = repair_loads(text)
|
|
169
|
+
except Exception: # a salvage failure is a malformed verdict — a third-party error never crosses the parse
|
|
170
|
+
raise _malformed(verdict_text) from None
|
|
171
|
+
|
|
172
|
+
if data == []: # an emptiness synthesized by the salvage — only an explicitly valid [] passes
|
|
173
|
+
raise _malformed(verdict_text) from None
|
|
174
|
+
|
|
175
|
+
if not isinstance(data, list):
|
|
176
|
+
raise _malformed(verdict_text)
|
|
177
|
+
|
|
178
|
+
findings: list[ComplianceFinding] = []
|
|
179
|
+
|
|
180
|
+
for item in data:
|
|
181
|
+
if not isinstance(item, dict):
|
|
182
|
+
raise _malformed(verdict_text)
|
|
183
|
+
|
|
184
|
+
instruction = item.get("instruction")
|
|
185
|
+
priority = item.get("priority")
|
|
186
|
+
explanation = item.get("explanation")
|
|
187
|
+
dimension = item.get("dimension")
|
|
188
|
+
|
|
189
|
+
if (
|
|
190
|
+
not isinstance(instruction, str)
|
|
191
|
+
or not isinstance(priority, str)
|
|
192
|
+
or not isinstance(explanation, str)
|
|
193
|
+
or not isinstance(dimension, str)
|
|
194
|
+
):
|
|
195
|
+
raise _malformed(verdict_text)
|
|
196
|
+
|
|
197
|
+
if priority not in COMPLIANCE_PRIORITIES:
|
|
198
|
+
raise _malformed(verdict_text)
|
|
199
|
+
|
|
200
|
+
if dimension not in COMPLIANCE_DIMENSIONS:
|
|
201
|
+
raise _malformed(verdict_text)
|
|
202
|
+
|
|
203
|
+
findings.append(
|
|
204
|
+
ComplianceFinding(instruction=instruction, priority=priority, explanation=explanation, dimension=dimension)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
return findings
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _degraded_diagnosis(text: str) -> GroupFailureClassification:
|
|
211
|
+
"""Build the conservative verdict of an unusable diagnosis answer.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
text: the trimmed raw answer that did not validate.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
The GroupFailureClassification carrying the conservative incurable,
|
|
218
|
+
the raw answer in root_cause and the degraded flag set.
|
|
219
|
+
"""
|
|
220
|
+
return GroupFailureClassification(
|
|
221
|
+
category="incurable",
|
|
222
|
+
root_cause=text,
|
|
223
|
+
earliest_step="",
|
|
224
|
+
recommendation=DEGRADED_GROUP_RECOMMENDATION,
|
|
225
|
+
degraded=True,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def parse_group_failure_classification(verdict_text: str) -> GroupFailureClassification:
|
|
230
|
+
"""Parse the raw answer of the group diagnosis request into the verdict.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
verdict_text: the raw text answer of the diagnosis model;
|
|
234
|
+
whitespace-padded JSON is tolerated.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
The parsed verdict; ``degraded`` is True only on the degradation
|
|
238
|
+
path — an unusable answer never raises and never grants
|
|
239
|
+
regeneration: it degrades to the conservative incurable; a syntax
|
|
240
|
+
failure is salvaged once through ``json_repair``.
|
|
241
|
+
"""
|
|
242
|
+
text = verdict_text.strip()
|
|
243
|
+
data: object = None
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
data = json.loads(text)
|
|
247
|
+
except ValueError:
|
|
248
|
+
try:
|
|
249
|
+
data = repair_loads(text) # syntax salvage of a model glitch
|
|
250
|
+
except Exception: # a salvage failure degrades — a third-party exception never crosses the parse
|
|
251
|
+
return _degraded_diagnosis(text)
|
|
252
|
+
|
|
253
|
+
if isinstance(data, dict):
|
|
254
|
+
category = data.get("category")
|
|
255
|
+
root_cause = data.get("root_cause")
|
|
256
|
+
earliest_step = data.get("earliest_step")
|
|
257
|
+
recommendation = data.get("recommendation")
|
|
258
|
+
|
|
259
|
+
if (
|
|
260
|
+
isinstance(category, str)
|
|
261
|
+
and isinstance(root_cause, str)
|
|
262
|
+
and isinstance(earliest_step, str)
|
|
263
|
+
and isinstance(recommendation, str)
|
|
264
|
+
and category in GROUP_CATEGORIES
|
|
265
|
+
):
|
|
266
|
+
return GroupFailureClassification(
|
|
267
|
+
category=category,
|
|
268
|
+
root_cause=root_cause,
|
|
269
|
+
earliest_step=earliest_step,
|
|
270
|
+
recommendation=recommendation,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return _degraded_diagnosis(text)
|