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
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Group recovery
|
|
2
|
+
|
|
3
|
+
Domain: the diagnosis-driven recovery of step groups. Audience: library internals and engineers
|
|
4
|
+
reasoning about recovered group runs.
|
|
5
|
+
|
|
6
|
+
## When recovery runs
|
|
7
|
+
|
|
8
|
+
Every classification point of a group step on a non-strict run routes here instead of the per-step
|
|
9
|
+
classification-and-heal: a failed check of a generation candidate, a failed cached replay and the
|
|
10
|
+
generation-budget exhaustion. Inside a group there is no isolated per-step healing. Strict mode
|
|
11
|
+
never runs recovery — the classification-only path stays.
|
|
12
|
+
|
|
13
|
+
## The cycle
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from prettyplay.engine.renderer import PreparedStep
|
|
17
|
+
|
|
18
|
+
healed = recovery.recover(
|
|
19
|
+
group_prompt="accept cookies, fill and submit the order form",
|
|
20
|
+
traces=group_traces, # GroupStepOutcome per group step, execution order
|
|
21
|
+
prepared=PreparedStep(instruction="the status shows order confirmed"), # the render product of the failed step
|
|
22
|
+
step_type="assertion",
|
|
23
|
+
previous_steps=scenario, # the typed scenario records of the test, execution order
|
|
24
|
+
identity=identity,
|
|
25
|
+
attempt_history=history,
|
|
26
|
+
page=page,
|
|
27
|
+
window=window,
|
|
28
|
+
memory=memory,
|
|
29
|
+
)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
1. One group-level diagnosis request — the classification model and instructions, the group
|
|
33
|
+
prompt, the step traces with outcomes and URL transitions, the current page state, the failed
|
|
34
|
+
step's attempt history. Labels: `recoverable | product_defect | incurable`; a garbage answer
|
|
35
|
+
degrades to incurable with the raw answer logged.
|
|
36
|
+
2. `product_defect` fails loudly; `incurable` fails terminally; a root named outside the group
|
|
37
|
+
ends in the honest terminal failure naming that step.
|
|
38
|
+
3. `recoverable`: the row runs from the earliest affected group step through the failed step —
|
|
39
|
+
each step is
|
|
40
|
+
re-rendered against the current context (its template sentence, its recorded inputs, the current memory),
|
|
41
|
+
regenerates as its own unit (the diagnosis recommendation + the group framing ride the request), re-executes
|
|
42
|
+
immediately on the current page, validates its returned result, passes the two-dimension compliance gate,
|
|
43
|
+
publishes its captures to the test memory and writes back to the cache per step.
|
|
44
|
+
4. A repeat failure re-enters a fresh diagnosis and a new cycle while cycles remain; every new
|
|
45
|
+
cycle grants each row step a fresh full healing counter; the number of cycles per group is
|
|
46
|
+
capped by `healing_attempts` — never infinite.
|
|
47
|
+
|
|
48
|
+
## Reporting
|
|
49
|
+
|
|
50
|
+
Diagnosis and regeneration requests use the provider's bounded transport retries. Resending
|
|
51
|
+
an identical request consumes no additional group cycle or per-step healing attempt. A final
|
|
52
|
+
`LLMUnavailableError` propagates out of recovery; it never opens another recovery cycle.
|
|
53
|
+
|
|
54
|
+
The block itself reports through the four group lifecycle hook events — `on_group_started`,
|
|
55
|
+
then `on_group_passed` (a recovered group reports passed) or `on_group_failed`, closed by
|
|
56
|
+
`on_group_finished`. `on_healing_started` / `on_healed` fire per recovered step; the diagnosis
|
|
57
|
+
itself and the row composition stay log-only records.
|
|
58
|
+
|
|
59
|
+
## Memory and re-render
|
|
60
|
+
|
|
61
|
+
- Row steps re-render through `render_step` — recovery never reuses a stale instruction: the current memory and
|
|
62
|
+
the step's recorded inputs produce the fresh one
|
|
63
|
+
- Recovery keeps local copies of previous scenario records and group traces. After each accepted row step, its
|
|
64
|
+
newly prepared instruction replaces that occurrence in the local views used by later row requests and diagnoses;
|
|
65
|
+
the test's stored records and appended traces remain immutable
|
|
66
|
+
- A failed row regeneration becomes the active failure for the next diagnosis: STEP and HISTORY describe that row
|
|
67
|
+
step and its latest attempt; a complete row still returns the healed code of the originally failed step
|
|
68
|
+
- Every accepted row step publishes its captures immediately; a later failure in the row never rolls back accepted
|
|
69
|
+
values — steps left behind keep their last accepted observations
|
|
70
|
+
- Traces carry the instruction (what the model saw and what the row matcher quotes) and the original `vars` of
|
|
71
|
+
each step's call
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Facade of the prettyplay.engine.groups cell: the diagnosis-driven recovery of step groups."""
|
|
2
|
+
|
|
3
|
+
from .diagnosis import classify_group_failure
|
|
4
|
+
from .outcome import GroupStepOutcome
|
|
5
|
+
from .recovery import GroupRecovery
|
|
6
|
+
|
|
7
|
+
__all__ = ["GroupRecovery", "GroupStepOutcome", "classify_group_failure"]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""The single group diagnosis request: the whole interaction in view, one verdict."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from ...config import Config
|
|
6
|
+
from ...driver import PageFacade
|
|
7
|
+
from ...llm import GroupFailureClassification, LLMProvider, ScenarioStep
|
|
8
|
+
from ..attempts import StepAttempt
|
|
9
|
+
from .outcome import GroupStepOutcome
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("prettyplay")
|
|
12
|
+
|
|
13
|
+
#: System prompt of every group diagnosis request; applied verbatim by the provider.
|
|
14
|
+
#: Frozen mirror of ``.goga/usages/prompts/group_diagnosis.md`` (the section after the ``---``
|
|
15
|
+
#: separator) — the single source of the prompt; the constant changes only together with the file.
|
|
16
|
+
GROUP_DIAGNOSIS_PROMPT = """\
|
|
17
|
+
You diagnose a failure of one step inside a group of steps that form one coherent interaction with
|
|
18
|
+
a shared goal.
|
|
19
|
+
|
|
20
|
+
Input you receive:
|
|
21
|
+
- GROUP PROMPT: the shared goal of the group, verbatim
|
|
22
|
+
- PREVIOUS STEPS: the prepared instructions of the prior scenario of the test, in execution order —
|
|
23
|
+
the group entries marked
|
|
24
|
+
- GROUP STEPS: every step of the group in execution order — each with its recorded prepared
|
|
25
|
+
instruction, its outcome (passed or failed) and its URL before -> after transition
|
|
26
|
+
- STEP: the failed step's prepared instruction
|
|
27
|
+
- HISTORY: the verbatim record of every attempt of the failed step so far
|
|
28
|
+
- PAGE SNAPSHOT: the accessibility snapshot of the current page
|
|
29
|
+
- SCREENSHOT: an image of the page, when attached
|
|
30
|
+
- USER INSTRUCTIONS: the project's binding classification guidance, when configured — follow it;
|
|
31
|
+
it never overrides the fixed answer format below
|
|
32
|
+
|
|
33
|
+
Answer with exactly one JSON object of the form:
|
|
34
|
+
{"category": "recoverable | product_defect | incurable",
|
|
35
|
+
"root_cause": "<one short sentence>",
|
|
36
|
+
"earliest_step": "<the verbatim sentence of the earliest affected step>",
|
|
37
|
+
"recommendation": "<one short sentence what should be regenerated or done>"}
|
|
38
|
+
|
|
39
|
+
Category calibration:
|
|
40
|
+
- recoverable — the failure stems from one or more earlier steps of the group (a step that did not
|
|
41
|
+
land what its sentence says) and regenerating the affected steps of the group can fix the run
|
|
42
|
+
- product_defect — the application is genuinely broken; regenerating steps cannot and must not
|
|
43
|
+
turn this green
|
|
44
|
+
- incurable — the root cannot be reached from inside the group (it lives in an earlier step of the
|
|
45
|
+
test outside the group), or regeneration cannot help
|
|
46
|
+
|
|
47
|
+
Rules:
|
|
48
|
+
- earliest_step must quote a visible instruction verbatim: a group step's instruction when the
|
|
49
|
+
category is recoverable; when the root lives outside the group, quote that outside step's
|
|
50
|
+
instruction from PREVIOUS STEPS verbatim and answer incurable; raw template sentences are not
|
|
51
|
+
request content
|
|
52
|
+
- Output only the JSON object, no other text"""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def classify_group_failure( # noqa: PLR0913, PLR0917 — the parameter list is fixed by the recovery contract
|
|
56
|
+
config: Config,
|
|
57
|
+
provider: LLMProvider,
|
|
58
|
+
group_prompt: str,
|
|
59
|
+
traces: list[GroupStepOutcome],
|
|
60
|
+
previous_steps: list[ScenarioStep],
|
|
61
|
+
step_text: str,
|
|
62
|
+
step_type: str, # noqa: ARG001 — the contract parameter; the kind rides the traces, not the port call
|
|
63
|
+
attempt_history: list[StepAttempt],
|
|
64
|
+
page: PageFacade,
|
|
65
|
+
) -> GroupFailureClassification:
|
|
66
|
+
"""Diagnose a failed group step with the whole interaction in view.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
config: project settings; ``send_screenshots`` attaches the page
|
|
70
|
+
image and ``classification_prompt`` supplies the user
|
|
71
|
+
instructions of the request.
|
|
72
|
+
provider: the LLM port implementation diagnosing the failure.
|
|
73
|
+
group_prompt: the group prompt of the diagnosed group, verbatim.
|
|
74
|
+
traces: the group's step traces in execution order — every record
|
|
75
|
+
renders its recorded prepared instruction into the GROUP STEPS
|
|
76
|
+
block; the raw sentences stay local.
|
|
77
|
+
previous_steps: the typed scenario records of the test, in
|
|
78
|
+
execution order — the provider renders their prepared
|
|
79
|
+
instructions as the PREVIOUS STEPS block, the group entries
|
|
80
|
+
marked; the basis of the outside-group root diagnosis.
|
|
81
|
+
step_text: the prepared instruction of the failed step, verbatim —
|
|
82
|
+
never the raw template sentence.
|
|
83
|
+
step_type: the step kind: action or assertion.
|
|
84
|
+
attempt_history: the per-step attempt history of the failed step,
|
|
85
|
+
grown to the failure — every record rendered verbatim into the
|
|
86
|
+
HISTORY block.
|
|
87
|
+
page: the page facade of the current test.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
The diagnosis verdict — a garbage answer arrives as the
|
|
91
|
+
conservative incurable, never as a granted regeneration.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
LLMUnavailableError: the provider service failed after the bounded
|
|
95
|
+
transport retries; an explicit infrastructure failure — the
|
|
96
|
+
calling recovery decides.
|
|
97
|
+
"""
|
|
98
|
+
snapshot = page.aria_snapshot()
|
|
99
|
+
screenshot = page.screenshot() if config.send_screenshots else None
|
|
100
|
+
|
|
101
|
+
verdict = provider.classify_group_failure(
|
|
102
|
+
prompt=GROUP_DIAGNOSIS_PROMPT,
|
|
103
|
+
user_instructions=config.classification_prompt,
|
|
104
|
+
group_prompt=group_prompt,
|
|
105
|
+
group_steps=[trace.render() for trace in traces],
|
|
106
|
+
previous_steps=previous_steps,
|
|
107
|
+
step_text=step_text,
|
|
108
|
+
attempt_history=[record.render() for record in attempt_history],
|
|
109
|
+
snapshot=snapshot,
|
|
110
|
+
screenshot=screenshot,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if verdict.degraded:
|
|
114
|
+
logger.warning(
|
|
115
|
+
"group_diagnosis_degraded",
|
|
116
|
+
extra={"group": group_prompt, "answer": verdict.root_cause},
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
logger.info(
|
|
120
|
+
"group_diagnosed",
|
|
121
|
+
extra={
|
|
122
|
+
"group": group_prompt,
|
|
123
|
+
"category": verdict.category,
|
|
124
|
+
"earliest": verdict.earliest_step,
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return verdict
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""The verbatim trace record of a group step: the unit the GROUP STEPS block renders."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict
|
|
4
|
+
|
|
5
|
+
from ...cache import StepIdentity
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GroupStepOutcome(BaseModel):
|
|
9
|
+
"""One verbatim trace record of a group step's execution — never rewritten or truncated.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
sentence: the raw sentence of the group step (the template source),
|
|
13
|
+
verbatim — the re-render source of the row mechanics, never
|
|
14
|
+
rendered into requests.
|
|
15
|
+
instruction: the prepared instruction recorded at the step's
|
|
16
|
+
execution — what the GROUP STEPS block renders and the matcher
|
|
17
|
+
matches against; equals ``sentence`` for a non-template step.
|
|
18
|
+
step_type: the step kind: action or assertion.
|
|
19
|
+
tries: the declared retry count of the step; None — the step is
|
|
20
|
+
governed by the global polling settings.
|
|
21
|
+
delay: the declared start pause of the step in seconds; None — no
|
|
22
|
+
pause.
|
|
23
|
+
vars: the call-local input bindings of the step's original call —
|
|
24
|
+
the re-render input of the row mechanics.
|
|
25
|
+
outcome: the execution outcome: passed or failed.
|
|
26
|
+
url_before: the page URL read immediately before the step's
|
|
27
|
+
execution.
|
|
28
|
+
url_after: the page URL read immediately after the step's execution.
|
|
29
|
+
identity: the cache address of the step — recorded by the executor
|
|
30
|
+
when the trace is appended; the addressing metadata of the row
|
|
31
|
+
mechanics, never rendered into the GROUP STEPS block.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(kw_only=True, frozen=True)
|
|
35
|
+
|
|
36
|
+
sentence: str = ""
|
|
37
|
+
instruction: str = ""
|
|
38
|
+
step_type: str = ""
|
|
39
|
+
tries: int | None = None
|
|
40
|
+
delay: float | None = None
|
|
41
|
+
vars: dict[str, str] = {}
|
|
42
|
+
outcome: str = ""
|
|
43
|
+
url_before: str = ""
|
|
44
|
+
url_after: str = ""
|
|
45
|
+
identity: StepIdentity
|
|
46
|
+
|
|
47
|
+
def render(self) -> str:
|
|
48
|
+
"""Render the complete verbatim trace — the unit the GROUP STEPS block renders.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
The multi-line trace text — the pinned three-line format:
|
|
52
|
+
instruction, outcome label, URL pair; the sentence and the
|
|
53
|
+
identity never render.
|
|
54
|
+
"""
|
|
55
|
+
return f"{self.instruction}\noutcome: {self.outcome}\nurl: {self.url_before} -> {self.url_after}"
|