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,515 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: prettyplay
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: AI-powered UI test generation tool
|
|
5
|
+
License: BSD-3-Clause
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: pydantic>=2.7
|
|
18
|
+
Requires-Dist: playwright>=1.49
|
|
19
|
+
Requires-Dist: openai>=1.30
|
|
20
|
+
Requires-Dist: anthropic>=0.28
|
|
21
|
+
Requires-Dist: json-repair>=0.63
|
|
22
|
+
Requires-Dist: jinja2>=3.1
|
|
23
|
+
Requires-Dist: tomli>=2.0; python_version < "3.11"
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
26
|
+
Requires-Dist: pytest-cov>=5.0; extra == "test"
|
|
27
|
+
Requires-Dist: pytest-mock>=3.10; extra == "test"
|
|
28
|
+
Requires-Dist: ruff>=0.15.0; extra == "test"
|
|
29
|
+
Provides-Extra: docs
|
|
30
|
+
Requires-Dist: mkdocs>=1.6.0; extra == "docs"
|
|
31
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# prettyplay
|
|
35
|
+
|
|
36
|
+
UI tests written as plain sentences. Each step sentence is turned into executable
|
|
37
|
+
code once — by an LLM, against the live page — and cached in the repository.
|
|
38
|
+
Every later run replays the cached code with no LLM involvement at all.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install prettyplay
|
|
44
|
+
playwright install # browser binaries for the driver
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires Python 3.10+.
|
|
48
|
+
|
|
49
|
+
Full documentation: <https://qarium.github.io/prettyplay/>
|
|
50
|
+
|
|
51
|
+
A complete pytest project — Allure reporting hooks included — lives in the
|
|
52
|
+
`example/` directory of the repository; it additionally requires
|
|
53
|
+
`allure-pytest` (`pip install allure-pytest`), which the core package does
|
|
54
|
+
not depend on.
|
|
55
|
+
|
|
56
|
+
## Quick start
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from prettyplay import PrettyPlay
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_login():
|
|
63
|
+
play = PrettyPlay("login-flow")
|
|
64
|
+
play.step("open the login page")
|
|
65
|
+
play.step("enter the login and password")
|
|
66
|
+
play.step("click the Sign in button")
|
|
67
|
+
play.expect("the Welcome message appears")
|
|
68
|
+
play.close()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or with the context manager:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
with PrettyPlay("login-flow") as play:
|
|
75
|
+
play.step("open the login page")
|
|
76
|
+
|
|
77
|
+
with play.group('Fill login form') as form:
|
|
78
|
+
form.step("enter the login and password")
|
|
79
|
+
form.step("click the Sign in button")
|
|
80
|
+
form.expect("the Welcome message appears")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Each `PrettyPlay` is fully self-contained: it owns its settings, its attempt
|
|
84
|
+
budgets and its own browser session. `close()` (or leaving the `with` block)
|
|
85
|
+
closes the page and stops the whole browser of that test, and every test
|
|
86
|
+
starts with fresh attempt budgets. The old process-wide
|
|
87
|
+
`prettyplay.get_runtime()` singleton was removed — build
|
|
88
|
+
`prettyplay.PrettyplayRuntime(config)` directly if you composed objects over
|
|
89
|
+
it.
|
|
90
|
+
|
|
91
|
+
The constructor arguments form the cache address: `cache_key` (mandatory) and
|
|
92
|
+
`cache_path` (optional subdirectory). Equal keys in the shared root reuse one
|
|
93
|
+
cached step across tests; a different language, step type or key is a
|
|
94
|
+
different step.
|
|
95
|
+
|
|
96
|
+
The third argument overrides settings per test — only the fields you pass
|
|
97
|
+
count: explicitly set values win over pyproject.toml and the environment,
|
|
98
|
+
everything else resolves from the file layer as before. Browser settings form
|
|
99
|
+
the nested `BrowserConfig` group; an empty string inside the group means unset,
|
|
100
|
+
untouched group defaults never overwrite the file values:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from prettyplay import BrowserConfig, PrettyConfig
|
|
104
|
+
|
|
105
|
+
t = PrettyPlay("login-flow", config=PrettyConfig(browser=BrowserConfig(name="firefox")))
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Screenshots belong to the author — nothing is captured automatically. Both
|
|
109
|
+
methods need a step to have run (the page opens lazily):
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
png = t.get_screenshot() # full-page PNG bytes
|
|
113
|
+
t.save_screenshot("artifacts/home.png") # write full-page PNG to a file
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Stateful page actions excluded from generated code — `page.route`,
|
|
117
|
+
`page.clock`, `add_init_script`, tracing, HAR, CDP — are the author's
|
|
118
|
+
explicit tools through the escape hatch, which runs inside the driver
|
|
119
|
+
worker thread against the genuine page (a step must have run first):
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
t.run_on_page(lambda page: page.route("**/api/config", lambda r: r.fulfill(json={"mode": "demo"})))
|
|
123
|
+
title = t.run_on_page(lambda page: page.title())
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The action must use the page API only: calling back into the test object
|
|
127
|
+
(`t.step`, screenshots, a nested `t.run_on_page`) marshals into the same
|
|
128
|
+
worker thread the action runs on — the worker rejects the re-entrant
|
|
129
|
+
crossing with a loud error instead of a deadlock.
|
|
130
|
+
|
|
131
|
+
## What happens on a step
|
|
132
|
+
|
|
133
|
+
Steps can capture page text for later steps. For example, call
|
|
134
|
+
`play.step("Read the item name into {% var name %}")`, then
|
|
135
|
+
`play.expect("The page shows {{ name }}")`. Call-local string values use a
|
|
136
|
+
separate namespace: `play.step("Open {{ vars.code }}", vars={"code": "A1"})`.
|
|
137
|
+
Captures must be nonblank strings read from the page, live for this test only,
|
|
138
|
+
and cannot be declared in `expect`. See
|
|
139
|
+
[Writing steps](docs/guides/writing-steps.md#templates-and-step-memory).
|
|
140
|
+
|
|
141
|
+
- **cache hit** — the cached code runs; no LLM is contacted
|
|
142
|
+
- **cache miss** — the step code is generated (a candidate that must actually
|
|
143
|
+
work on the page), then cached; only successes are cached — after the
|
|
144
|
+
compliance gate (instruction compliance and step adequacy — see
|
|
145
|
+
[Instructions](#instructions)). A candidate
|
|
146
|
+
assertion that legitimately fails stops the retries at once and is
|
|
147
|
+
classified: a real defect fails as `product_defect`; a `rot` or `fixable`
|
|
148
|
+
verdict grants exactly one healing-funded regeneration carrying the
|
|
149
|
+
classification recommendation
|
|
150
|
+
- **cached failure** — the failure is classified:
|
|
151
|
+
- `rot` (the UI changed) — the step is regenerated and the cache rewritten
|
|
152
|
+
- `fixable` (the step code is at fault — an ambiguous or wrong locator —
|
|
153
|
+
while the intent stays satisfiable) — regenerated for the same intent,
|
|
154
|
+
the request carrying the classification recommendation
|
|
155
|
+
- `product_defect` — the test fails loudly; nothing is regenerated
|
|
156
|
+
- `incurable` — the step fails with an explanation and a recommendation
|
|
157
|
+
|
|
158
|
+
A step authored inside a **group block** never takes this per-step path on a
|
|
159
|
+
non-strict run: its failure routes to the group recovery — one diagnosis of
|
|
160
|
+
the whole group drives a group-scoped regeneration row (see
|
|
161
|
+
[Step groups](#step-groups)).
|
|
162
|
+
|
|
163
|
+
Every execution of step code in the step cycle — cached code and
|
|
164
|
+
generation/healing candidates alike — runs under the **settle window** (see
|
|
165
|
+
[Settle polling](#settle-polling)): a transient page-state failure re-executes
|
|
166
|
+
the same code before any costly move. Steering-dialog turns are the one
|
|
167
|
+
exception — see [Interactive steering](#interactive-steering).
|
|
168
|
+
|
|
169
|
+
**Strict replay-only mode** (`strict = true`) never contacts the LLM for code:
|
|
170
|
+
a cache miss fails immediately as `IncurableStepError` ("strict mode forbids
|
|
171
|
+
generation — the step is missing from the cache"), and a failed cached step is
|
|
172
|
+
at most classified — the only LLM call strict mode ever makes — then raised by
|
|
173
|
+
its category (`product_defect` → `ProductDefectError`, everything else — rot
|
|
174
|
+
included — → `IncurableStepError`). Nothing is regenerated or healed, no
|
|
175
|
+
attempt budget is consumed and the cache is never written. The settle window
|
|
176
|
+
still applies to cached code — re-executing it is execution, not generation.
|
|
177
|
+
When the LLM is unavailable, the verdict is skipped with a `WARNING` and the
|
|
178
|
+
step type alone picks the error kind. This is the natural CI posture: generate
|
|
179
|
+
locally, run strict in the pipeline.
|
|
180
|
+
|
|
181
|
+
## Settle polling
|
|
182
|
+
|
|
183
|
+
`polling_timeout` (default `None` — off; `0` — explicit disable) opens one
|
|
184
|
+
settle window per step execution, measured from the first execution of the
|
|
185
|
+
step's code: a transient failure of a pollable kind — timeouts, element-state
|
|
186
|
+
races, navigation races, failed checks (a failed `expect(...)` chain or a
|
|
187
|
+
plain assert on an immediate read, like `assert videos.count() > 1`) —
|
|
188
|
+
re-executes the same code after `polling_delay` (default 0.5 s) until
|
|
189
|
+
success or window end. Attempts appear as `settle_retry` log records; no LLM
|
|
190
|
+
budget is consumed. Locator ambiguity and Python-level errors of the step
|
|
191
|
+
code (syntax, names, types) never poll. Size the window above the longest
|
|
192
|
+
auto-wait it must absorb (6.0 covers one exhausted 5 s expectation plus one
|
|
193
|
+
re-execution).
|
|
194
|
+
|
|
195
|
+
Every step also takes two keyword-only parameters: `tries` replaces the time
|
|
196
|
+
bound with a count bound for that step (`t.step("open the cart", tries=3)` —
|
|
197
|
+
at most 3 executions in total, the first included; exhaustion propagates the
|
|
198
|
+
failure, the step never stays green on retries alone), and `delay` is a
|
|
199
|
+
quiet pause in seconds before the step's code runs
|
|
200
|
+
(`t.expect("the total updates", delay=1.5)` — the step's `on_step_started`
|
|
201
|
+
event fires, then the pause, then the code). Invalid values fail loudly at
|
|
202
|
+
the call, before any page or LLM involvement.
|
|
203
|
+
|
|
204
|
+
## Step groups
|
|
205
|
+
|
|
206
|
+
Steps that form one coherent mini-scenario can be authored as a group with a
|
|
207
|
+
shared goal — the group prompt:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
with t.group("the checkout flow", speed=50, delay=2) as g:
|
|
211
|
+
g.step("accept the cookie banner")
|
|
212
|
+
g.step("fill the email field")
|
|
213
|
+
g.expect("the order confirmation appears")
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Inside the block the surface is the ordinary one — `g.step`/`g.expect` with
|
|
217
|
+
the `tries`/`delay` parameters — and group membership changes nothing about
|
|
218
|
+
addressing: a group step lands at the same cache address it would outside
|
|
219
|
+
the block, replays LLM-free, and steps outside the block are ordinary steps
|
|
220
|
+
(no ambient rerouting). What changes is failure handling on a non-strict run:
|
|
221
|
+
a failed group step is diagnosed once against the whole interaction — the
|
|
222
|
+
group prompt, every step trace and the failed step's attempt history — and
|
|
223
|
+
the verdict drives a group-scoped recovery row that regenerates the affected
|
|
224
|
+
steps forward on the current page, through the same compliance gate and
|
|
225
|
+
per-step cache write-back as every caching path. A diagnosed product defect
|
|
226
|
+
still fails the test loudly; a root outside the group ends the run honestly;
|
|
227
|
+
recovery cycles per group per test are capped (never an infinite loop). The
|
|
228
|
+
`speed`/`delay` group parameters pace the block — `delay` pauses quietly
|
|
229
|
+
before the first step, `speed` (0–100) inserts
|
|
230
|
+
`int((100 − speed) × 30)` ms between steps as a plain library wait.
|
|
231
|
+
|
|
232
|
+
See the [Groups reference](https://qarium.github.io/prettyplay/reference/groups/)
|
|
233
|
+
for the recovery cycle, the budgets and the log records.
|
|
234
|
+
|
|
235
|
+
## Interactive steering
|
|
236
|
+
|
|
237
|
+
`interactive = true` (default `false`) arms the steering REPL for local
|
|
238
|
+
generation sessions: when a step terminally fails with
|
|
239
|
+
`IncurableStepError`, a terminal dialog opens — step, failed code, the
|
|
240
|
+
terminal error render, the current page URL, screenshot path, commands —
|
|
241
|
+
and every engineer message drives one regeneration whose complete code is
|
|
242
|
+
shown for approval with `run? [y/N]` before it executes against the live
|
|
243
|
+
page: nothing runs unseen, and a rejected turn enters the history instead
|
|
244
|
+
of the page. Local commands serve the context without an LLM request:
|
|
245
|
+
`snapshot` (the full accessibility snapshot), `screenshot` (a full PNG
|
|
246
|
+
written to a temporary file, path printed), `error` and `code` (the stored
|
|
247
|
+
texts) and `quit`. A green turn heals the step and writes it back to the
|
|
248
|
+
cache — only after the compliance gate passes (a `high` finding in either
|
|
249
|
+
dimension — instruction or adequacy — never reaches the cache: the
|
|
250
|
+
violation joins the history and the prompt reopens); every completed turn
|
|
251
|
+
— executed, red or rejected — appends its full verbatim record (the
|
|
252
|
+
outcome, the URL before → after pair, the complete code, the complete
|
|
253
|
+
outcome) to the shared per-step attempt history of every later request;
|
|
254
|
+
quit, EOF, SIGINT or an unreadable stdin
|
|
255
|
+
raises the original terminal failure. The dialog never opens on
|
|
256
|
+
`product_defect`, in strict mode, or when the provider is down, and
|
|
257
|
+
consumes no budgets. Dialog openings, guidance lines and declines log at
|
|
258
|
+
info level as `steering_opened`, `steering_guidance` and
|
|
259
|
+
`steering_declined`. Keep it off in CI — an accidentally opened dialog
|
|
260
|
+
would hang the run. This is the steering dialog of a stuck step, not an
|
|
261
|
+
interactive host mode (IPython and Jupyter keep working as before).
|
|
262
|
+
|
|
263
|
+
## Seeing the scenario
|
|
264
|
+
|
|
265
|
+
Step sentences go to the `prettyplay` logger at info level. The library
|
|
266
|
+
configures no handlers — enable logging to see the scenario in the output:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
import logging
|
|
270
|
+
|
|
271
|
+
logging.basicConfig(level=logging.INFO) # plain unittest runs
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```ini
|
|
275
|
+
# pytest: --log-cli-level=INFO
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Configuration
|
|
279
|
+
|
|
280
|
+
The `[tool.prettyplay]` section of pyproject.toml:
|
|
281
|
+
|
|
282
|
+
```toml
|
|
283
|
+
[tool.prettyplay]
|
|
284
|
+
provider = "openai" # openai | anthropic
|
|
285
|
+
model = "gpt-5"
|
|
286
|
+
generation_model = "" # optional: empty -> model
|
|
287
|
+
classification_model = "" # optional: empty -> model
|
|
288
|
+
base_url = ""
|
|
289
|
+
cache_root = "" # empty -> <cwd>/.prettyplay/cache/
|
|
290
|
+
generation_prompt = "" # user instructions for generation; empty -> no instructions block
|
|
291
|
+
generation_approve = true # the two-dimension compliance gate (instructions + step adequacy) before caching; false -> the gate never runs
|
|
292
|
+
classification_prompt = "" # user instructions for classification; empty -> no instructions block
|
|
293
|
+
strict = false # true -> replay-only mode (no generation, no healing)
|
|
294
|
+
interactive = false # true -> the steering dialog on a terminally stuck step (local sessions)
|
|
295
|
+
generation_attempts = 3
|
|
296
|
+
healing_attempts = 2
|
|
297
|
+
llm_request_attempts = 3 # total sends per LLM request; 1 disables transport retries
|
|
298
|
+
polling_timeout = 6.0 # settle window seconds; omit -> off, 0.0 -> explicit disable
|
|
299
|
+
polling_delay = 0.5 # pause between settle re-executions
|
|
300
|
+
send_screenshots = false
|
|
301
|
+
|
|
302
|
+
[tool.prettyplay.browser]
|
|
303
|
+
name = "chromium" # chromium | firefox | webkit | chrome | msedge
|
|
304
|
+
screen = "" # "" | WxH | fullscreen | Playwright device name
|
|
305
|
+
headless = true # false -> run with a visible browser window
|
|
306
|
+
endpoint = "" # ws:// endpoint of a remote browser; empty -> local launch
|
|
307
|
+
accept_dialogs = false # true -> accept (else dismiss) dialogs no in-step capture claims
|
|
308
|
+
speed = 100 # pace of the run, 0-100 %; 100 -> full speed (default)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
`llm_request_attempts` applies to generation, classification, group diagnosis
|
|
312
|
+
and compliance requests. Its default is 3 total sends, including the first;
|
|
313
|
+
`1` disables retries. Set `PRETTYPLAY_LLM_REQUEST_ATTEMPTS` in the environment
|
|
314
|
+
or pass `PrettyConfig(llm_request_attempts=...)` for one test. Explicit test
|
|
315
|
+
values override the environment, which overrides pyproject.toml. Values must
|
|
316
|
+
be positive integers.
|
|
317
|
+
|
|
318
|
+
The old flat keys `browser`, `headless` and `browser_endpoint` at the
|
|
319
|
+
`[tool.prettyplay]` level are gone — a pre-1.0 hard break: the loader rejects
|
|
320
|
+
them with a `ConfigurationError` naming their new home inside
|
|
321
|
+
`[tool.prettyplay.browser]`, and the removed legacy env name `PRETTYPLAY_BROWSER`
|
|
322
|
+
fails the same way with a hint to use `PRETTYPLAY_BROWSER_NAME`.
|
|
323
|
+
|
|
324
|
+
`chrome` and `msedge` launch the locally installed browser through the
|
|
325
|
+
chromium engine; the browser must be installed on the machine.
|
|
326
|
+
|
|
327
|
+
### Screen modes
|
|
328
|
+
|
|
329
|
+
The `screen` setting of the browser group decides the context size:
|
|
330
|
+
|
|
331
|
+
- `""` — the Playwright default.
|
|
332
|
+
- `1280x720` (WxH) — a fixed viewport, in every launch mode.
|
|
333
|
+
- `fullscreen` — follows the maximized window on a local headed launch (the
|
|
334
|
+
chromium family launches with `--start-maximized`; firefox/webkit keep their
|
|
335
|
+
plain launch) and pins a fixed 1920x1080 viewport where no window exists —
|
|
336
|
+
headless and remote connects.
|
|
337
|
+
- anything else — a Playwright device name (`iPhone 13`, `Pixel 7`, ...):
|
|
338
|
+
the full device descriptor is applied. An unknown name fails loudly with the
|
|
339
|
+
closest registry names suggested.
|
|
340
|
+
|
|
341
|
+
### Instructions
|
|
342
|
+
|
|
343
|
+
A non-empty `generation_prompt` is sent verbatim as a `USER INSTRUCTIONS`
|
|
344
|
+
block with every generation and regeneration request — it steers the style of
|
|
345
|
+
the generated code (e.g. `prefer data-test-id attributes`), never the failure
|
|
346
|
+
classification. The instructions are binding, not advisory: while the
|
|
347
|
+
compliance gate is on (`generation_approve`, default `true`),
|
|
348
|
+
every successfully generated candidate passes an independent compliance check
|
|
349
|
+
of two dimensions before it is cached — instruction compliance with these
|
|
350
|
+
instructions, and step adequacy (the code must match what the step sentence
|
|
351
|
+
says for its step type — neither falling short nor exceeding it, judged from
|
|
352
|
+
the verbatim per-step attempt history of what was already tried) — one extra
|
|
353
|
+
LLM call through the effective classification model, never the model that
|
|
354
|
+
wrote the candidate; a `high` finding in either dimension fails the
|
|
355
|
+
attempt and the retry carries the violation with the grown history,
|
|
356
|
+
`medium` and `low` findings pass with a `WARNING`, and a malformed verdict is
|
|
357
|
+
a loud `ComplianceVerdictError` (the candidate is never cached unchecked).
|
|
358
|
+
`generation_approve = false` (env `PRETTYPLAY_GENERATION_APPROVE`) removes the
|
|
359
|
+
gate entirely — the old behavior. The instructions are not part of the cache
|
|
360
|
+
address: changing them never invalidates cached steps — a cached step runs
|
|
361
|
+
unchanged and is never re-gated, so changed instructions need a manual cache
|
|
362
|
+
purge to take effect.
|
|
363
|
+
|
|
364
|
+
A non-empty `classification_prompt` works the same way for classification
|
|
365
|
+
requests only — it steers the verdict explanations (e.g. `answer in Russian`),
|
|
366
|
+
never generation, and never invalidates cached steps either.
|
|
367
|
+
|
|
368
|
+
### Remote browsers
|
|
369
|
+
|
|
370
|
+
A non-empty `endpoint` (e.g. `ws://ci-grid:3000/playwright/chromium`) connects
|
|
371
|
+
to a remote Playwright Server or browser grid instead of launching locally:
|
|
372
|
+
`headless` does not apply to a connect (window visibility belongs to the
|
|
373
|
+
endpoint server) and `chrome`/`msedge` map to the chromium engine — channels
|
|
374
|
+
are a local-launch concept. A non-empty endpoint must be a valid ws/wss URL
|
|
375
|
+
(otherwise `ConfigurationError` names the setting), and a failed connect fails
|
|
376
|
+
loudly with the endpoint in the message.
|
|
377
|
+
|
|
378
|
+
### Environment overrides
|
|
379
|
+
|
|
380
|
+
Every setting has a `PRETTYPLAY_<SETTING_UPPER>` environment override for CI —
|
|
381
|
+
including `PRETTYPLAY_STRICT` and `PRETTYPLAY_CLASSIFICATION_PROMPT` — and the
|
|
382
|
+
browser group reads the flat `PRETTYPLAY_BROWSER_NAME`,
|
|
383
|
+
`PRETTYPLAY_BROWSER_SCREEN`, `PRETTYPLAY_BROWSER_HEADLESS`,
|
|
384
|
+
`PRETTYPLAY_BROWSER_ENDPOINT`, `PRETTYPLAY_BROWSER_ACCEPT_DIALOGS` and
|
|
385
|
+
`PRETTYPLAY_BROWSER_SPEED`. Env
|
|
386
|
+
values parse by the field type: booleans
|
|
387
|
+
accept `true/false/1/0` case-insensitively, integers parse as decimals, floats
|
|
388
|
+
(the polling settings) parse as decimal floats, and an
|
|
389
|
+
unparseable value fails loudly with a `ConfigurationError` naming the setting,
|
|
390
|
+
the received value and the accepted form.
|
|
391
|
+
|
|
392
|
+
### Pace
|
|
393
|
+
|
|
394
|
+
`speed` of the browser group (0–100 inclusive, default 100) controls how fast
|
|
395
|
+
the browser executes the run: 100 — full speed, lower values slow the run
|
|
396
|
+
down linearly through Playwright's native `slow_mo`
|
|
397
|
+
(`int((100 − speed) × 30)` ms between browser operations). One value per
|
|
398
|
+
test, fixed at browser start in every launch mode — local headed, local
|
|
399
|
+
headless and remote connects alike. An out-of-range or malformed value fails
|
|
400
|
+
at configuration load naming `browser.speed`, the received value and the
|
|
401
|
+
accepted form (an integer 0-100 inclusive).
|
|
402
|
+
|
|
403
|
+
An invalid setting fails loudly with a `ConfigurationError`: one line per
|
|
404
|
+
setting — the name, the received value and the allowed values.
|
|
405
|
+
|
|
406
|
+
LLM API keys are never stored in the config file: they come only from the
|
|
407
|
+
environment — `OPENAI_API_KEY` for openai, `ANTHROPIC_API_KEY` for anthropic —
|
|
408
|
+
and are read lazily on the first request.
|
|
409
|
+
|
|
410
|
+
### Dialogs
|
|
411
|
+
|
|
412
|
+
`accept_dialogs` of the browser group controls how the resolver of last
|
|
413
|
+
resort settles unclaimed dialogs:
|
|
414
|
+
|
|
415
|
+
- `true` — every dialog that no in-step stock dialog capture claims is
|
|
416
|
+
accepted
|
|
417
|
+
- `false` (default) — unclaimed dialogs are dismissed (the Playwright
|
|
418
|
+
default outcome)
|
|
419
|
+
- the resolution runs at the tail of the driver-thread run unit, not at
|
|
420
|
+
the moment the dialog fires: a dialog unclaimed by the step blocks the
|
|
421
|
+
page until the unit ends, which can fail the remainder of the step
|
|
422
|
+
- a dialog claimed by a step's in-step stock capture is accepted or dismissed
|
|
423
|
+
by the step itself — the setting does not apply to captured dialogs
|
|
424
|
+
|
|
425
|
+
## Failure taxonomy
|
|
426
|
+
|
|
427
|
+
Every library failure derives from `PrettyplayError`:
|
|
428
|
+
|
|
429
|
+
| Exception | Meaning | Recommended reaction |
|
|
430
|
+
|---|---|---|
|
|
431
|
+
| ProductDefectError | real product regression | treat as a bug: this failure is the value of the suite |
|
|
432
|
+
| IncurableStepError | the step cannot be (re)generated — budget exhausted, step text stale, or a strict-mode cache miss | follow `recommendation`: reword the step or refresh the cache |
|
|
433
|
+
| LLMUnavailableError | LLM infrastructure down | restore provider access; cached steps are unaffected |
|
|
434
|
+
| ComplianceVerdictError | the compliance gate could not obtain a usable verdict | rerun the step to retry generation; the candidate was never cached |
|
|
435
|
+
| ConfigurationError | invalid `[tool.prettyplay]` settings | fix the named setting — the message lists received and allowed values |
|
|
436
|
+
|
|
437
|
+
`ProductDefectError` and `IncurableStepError` render one structured terminal
|
|
438
|
+
message — the first line `ClassName: reason`, a `---` separated
|
|
439
|
+
`step:`/`error:` block (the `error:` line carries the decomposed headline
|
|
440
|
+
of the underlying error), a conditional `---` separated details section
|
|
441
|
+
(`received:`, `cause:`, `Call log:` — present only when the underlying
|
|
442
|
+
error carries them), and a `---` separated verdict block with
|
|
443
|
+
`explanation:` and `recommendation:` labels at column zero (the `category`
|
|
444
|
+
travels in the structured fields, never in the render). The
|
|
445
|
+
same single text feeds the exception message, the log record and the
|
|
446
|
+
`on_step_failed` hook payload — consumers never re-compose it. The verdict
|
|
447
|
+
fields also arrive through the `on_step_verdict` hook. `IncurableStepError`
|
|
448
|
+
also carries a `code` attribute — the step code that terminally failed (the
|
|
449
|
+
cached code on healing and strict paths, the last candidate on generation);
|
|
450
|
+
a field for programmatic consumers only, never rendered and never in hook
|
|
451
|
+
payloads.
|
|
452
|
+
`ProductDefectError` also derives from `AssertionError`, so any runner counts
|
|
453
|
+
it as a failed test, never an error. Tracebacks of library failures are folded
|
|
454
|
+
at the `t.step(...)` / `t.expect(...)` call site: internal engine frames
|
|
455
|
+
never appear in what the runner shows.
|
|
456
|
+
|
|
457
|
+
A healed run never turns a `ProductDefectError` into a green test.
|
|
458
|
+
|
|
459
|
+
## Hooks
|
|
460
|
+
|
|
461
|
+
```python
|
|
462
|
+
from prettyplay.reporting import StepHooks
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
class Reporter(StepHooks):
|
|
466
|
+
def on_step_started(self, step_text: str, step_type: str) -> None: ...
|
|
467
|
+
def on_step_passed(self, step_text: str, step_type: str) -> None: ...
|
|
468
|
+
def on_step_failed(self, step_text: str, step_type: str, error: str) -> None: ...
|
|
469
|
+
def on_step_verdict(self, step_text: str, category: str, explanation: str, recommendation: str) -> None: ...
|
|
470
|
+
def on_step_finished(self, step_text: str, step_type: str, outcome: str) -> None: ...
|
|
471
|
+
def on_generation_started(self, step_text: str, attempt: int) -> None: ...
|
|
472
|
+
def on_healing_started(self, step_text: str, category: str) -> None: ...
|
|
473
|
+
def on_healed(self, step_text: str, explanation: str) -> None: ...
|
|
474
|
+
def on_cache_saved(self, step_text: str, filename: str) -> None: ...
|
|
475
|
+
def on_cache_skipped(self, step_text: str, reason: str) -> None: ...
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
t = PrettyPlay("login-flow")
|
|
479
|
+
t.add_hooks(Reporter())
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Hooks can also be wired at construction — pass the list to the keyword-only
|
|
483
|
+
`hooks` parameter (`PrettyPlay("login-flow", hooks=[Reporter()])`) and every
|
|
484
|
+
event of every step reaches them. `StepHooks` is re-exported from the package
|
|
485
|
+
root, so `from prettyplay import StepHooks` works too.
|
|
486
|
+
|
|
487
|
+
For template steps, lifecycle hook `step_text` is the authored template;
|
|
488
|
+
generation and healing hook `step_text` is the rendered instruction.
|
|
489
|
+
|
|
490
|
+
A raising hook never fails the run; the failure is logged. The `error` payload
|
|
491
|
+
of `on_step_failed` is the full structured render of the failure — multi-line,
|
|
492
|
+
verbatim. `on_step_verdict` fires after `on_step_failed`, only when the
|
|
493
|
+
terminal failure carried a real verdict (never for the render-only fallback
|
|
494
|
+
recommendation of a verdict-less `IncurableStepError`).
|
|
495
|
+
|
|
496
|
+
## Cache and CI workflow
|
|
497
|
+
|
|
498
|
+
The cache lives under `.prettyplay/cache/` as plain Python files — one per
|
|
499
|
+
step, carrying its metadata (step sentence, cache key, step type, creation
|
|
500
|
+
date) and the step code. The code targets the standard Playwright sync API,
|
|
501
|
+
so cached steps keep replaying across library upgrades — the one recorded
|
|
502
|
+
break is the switch to the genuine page: caches written against the retired
|
|
503
|
+
page facade call methods that no longer exist and fail at replay; purge the
|
|
504
|
+
cache directory once and regenerate when upgrading across that change.
|
|
505
|
+
|
|
506
|
+
Generate locally where the LLM is reachable → commit the cache directory →
|
|
507
|
+
CI runs the whole suite from the cache with no LLM keys at all — `strict = true`
|
|
508
|
+
turns that posture into a guarantee: a cache miss fails the run instead of
|
|
509
|
+
silently generating.
|
|
510
|
+
|
|
511
|
+
## Limitations
|
|
512
|
+
|
|
513
|
+
Step sentences, group prompts, call-local `vars` values and captured values can
|
|
514
|
+
land in LLM prompts, diagnostic output and logs. Do not use them for secrets or
|
|
515
|
+
sensitive personal data.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
prettyplay/__init__.py,sha256=91ebop-G0VTKlW200V8dwzf3GzFvcfeQFavT1XBWrOs,526
|
|
2
|
+
prettyplay/executor.py,sha256=wOfNb5ARY_m24IijZuS2zukruS5tcF9GcEDWeKth3Ao,28773
|
|
3
|
+
prettyplay/groups.py,sha256=YW6zPuLY6dvijwIaAfDl3iV3GoHKf8LGMzrumIy3bCE,11242
|
|
4
|
+
prettyplay/runtime.py,sha256=vcbE5rNyb-Ntph9OSk0S70sep-L_o3ft3OPFhNXJxLE,2643
|
|
5
|
+
prettyplay/scenario.py,sha256=K_r2RfCWrRUEyuDQdhC6xKp2bzoKlpUNTNmK-QOIM4k,18269
|
|
6
|
+
prettyplay/.usages/lifecycle.md,sha256=5nhN9ihvLQiU0jGeBJUZg9vMGHQemLjCjpobDRHcQII,9263
|
|
7
|
+
prettyplay/.usages/steps.md,sha256=BtzefTSh6L6Kj_0-LaK1dRXYUc-5iAXdY8vVUP-2qkA,9134
|
|
8
|
+
prettyplay/cache/__init__.py,sha256=GYhRRVLAlRCdbKuzo-bMqmaBd4UqQXwch7RUG9zbDO0,318
|
|
9
|
+
prettyplay/cache/budgets.py,sha256=fCF3FXqgJrajqs8v5pAzHiGQStVk8EfEtuJLh3-aCZE,3451
|
|
10
|
+
prettyplay/cache/models.py,sha256=z5FwScZiSitTUDQ-DpOEmD3EhjXsbWTpNYD9X5NX4no,1746
|
|
11
|
+
prettyplay/cache/store.py,sha256=LFfqgSu9CPy42XmS_ZIErCXuNt_v-dgTGdq9kLTCu7c,9944
|
|
12
|
+
prettyplay/cache/text.py,sha256=ijc8iNqtz6IsyHOVmGQZ6Vf40nJJ-SnVyGFozslsBcM,1246
|
|
13
|
+
prettyplay/cache/.usages/addressing.md,sha256=A97hXPUWszYI5RoOeYii6nyMCa9ORwNsoo0hA6wd7r0,2128
|
|
14
|
+
prettyplay/cache/.usages/budgets.md,sha256=shf_By8BJYgAjoN129dBVMAZx5TeDxDO0fpPlsyl3z8,2579
|
|
15
|
+
prettyplay/cache/.usages/storage.md,sha256=uszgU-ExTKK-U2QOiwEL6JQV--Uh2tMM3BnLIhe9T-I,1554
|
|
16
|
+
prettyplay/config/__init__.py,sha256=noOu3FPBlUsQPcSntZswvX3uGNCiEmy54Q9moZh5eHs,273
|
|
17
|
+
prettyplay/config/loader.py,sha256=Yz2ArvPjaQhQRTKfAk_iyCmYRue6QWFULAh03yqxmQE,12041
|
|
18
|
+
prettyplay/config/models.py,sha256=7zh-wsO5foeCRMn-abpFICH5bNtUb5drpM4gSknxJdE,12330
|
|
19
|
+
prettyplay/config/.usages/configuration.md,sha256=FSeTXf0XV1u_yqe3JCvuz7gSXSat7xwAJcGzRUAajHY,13844
|
|
20
|
+
prettyplay/driver/__init__.py,sha256=qAO_BViG0ZxcImMFEkEOe8aYjrGtLfRI8NW3IO0vv50,272
|
|
21
|
+
prettyplay/driver/errors.py,sha256=SPeXGksloHQ1iHlSYQ_LEROhkTnNws10TUkxBDWIIXg,2666
|
|
22
|
+
prettyplay/driver/page.py,sha256=8GBJnLskjqs5NtgU0hmjUHAEgkZQRRPg4-nDKVa4bxk,6875
|
|
23
|
+
prettyplay/driver/session.py,sha256=adEJKwYm7llCTiJLN51exeT9S7WcFxzU74DCs53BvRE,12632
|
|
24
|
+
prettyplay/driver/.usages/error_kinds.md,sha256=becPikNRl7T3CISTFOO0qETOfzF6l01C-zRPD5qP4I4,2158
|
|
25
|
+
prettyplay/driver/.usages/plumbing.md,sha256=aNenMQfkcm7-KmO1E7j55oWEVcsCBSVpVfqbIXTj-hg,3096
|
|
26
|
+
prettyplay/engine/__init__.py,sha256=-VIaet3jxyzZi987NFy5KuvVE78b7r1TACRnXRp_bWY,541
|
|
27
|
+
prettyplay/engine/attempts.py,sha256=I3aoIPiWpNo1ZKSRF5jTUE702_42Aj0SqFRH_iKCSZQ,2771
|
|
28
|
+
prettyplay/engine/classification.py,sha256=-6VH_-76fPI8wRpxtdQqpmZ3gXPfec6Jm1KJcnygoBE,3024
|
|
29
|
+
prettyplay/engine/compliance.py,sha256=Q5FzfYfmQSTBMk2vIjCdrRvwJbFXn0dFHHa2TjOiNyI,7032
|
|
30
|
+
prettyplay/engine/execution.py,sha256=2mrWsv9ULZxMYbYBFQSyDTXAdJoLcg9XuuIPUehz46k,1977
|
|
31
|
+
prettyplay/engine/generator.py,sha256=VAYYLlu24DM9L_Oi0kLPSgBbdAkMV_EAATb-_gOHg7U,53986
|
|
32
|
+
prettyplay/engine/healer.py,sha256=DeGUh6F1NlnZNDkF1agN0y660-Pl7feysExkIOQo1EA,7124
|
|
33
|
+
prettyplay/engine/text.py,sha256=SQuVzz9gTD4A88jycXu5HzVuZ9-SWMfIXlMFT4RdxjQ,1516
|
|
34
|
+
prettyplay/engine/.usages/generation.md,sha256=6ymO9Fog7MKaX_qh8eeb4KUu144ccTdJvWf81Ko3zvU,10890
|
|
35
|
+
prettyplay/engine/.usages/healing.md,sha256=SxuI7hE8yL4dyuD0W8mJuBIB40ctz8xm3TnM5ikk3CE,4367
|
|
36
|
+
prettyplay/engine/groups/__init__.py,sha256=2D2F2MZYXs92b8ZvfhB6jG8-KKj-zUrvMm7bLr4b7pQ,293
|
|
37
|
+
prettyplay/engine/groups/diagnosis.py,sha256=9PT5PGj32lkJhjxpGYow9SJ-ohI8kPfs4MLw0sin0MU,5760
|
|
38
|
+
prettyplay/engine/groups/outcome.py,sha256=gcWGYa9zVNH1bqCn5pB2TIPnygmtP92G_UVDMopPKAU,2336
|
|
39
|
+
prettyplay/engine/groups/recovery.py,sha256=PlGuVG7UAmG8T8nWA9R4HoqVbBi_wWSBXMIYeMMSA9Y,22241
|
|
40
|
+
prettyplay/engine/groups/.usages/recovery.md,sha256=v7Oi2e1tDGCUgeHBuNsBcUcKDzWxERIkDjiwsJpbWto,4020
|
|
41
|
+
prettyplay/engine/polling/__init__.py,sha256=aEbl8NFKBLgPnvoSZZKVgmc5gUogcnfuWuwmmt49mhk,196
|
|
42
|
+
prettyplay/engine/polling/settle.py,sha256=oqaLXegMWK5qMQ2ZGx0PbBiiLkwPsAuKaok20WWwzoA,3303
|
|
43
|
+
prettyplay/engine/polling/window.py,sha256=_kq3T_JlFDIda2ZHgwVeAMkNfZ7xWGY0CNs7mk_6tPI,2932
|
|
44
|
+
prettyplay/engine/polling/.usages/settle.md,sha256=VqWCCEw3B0pe4S8uLb4hV00vhjr9GRNj9T6I78cjgsU,3162
|
|
45
|
+
prettyplay/engine/renderer/__init__.py,sha256=q4OTSJX83TUm7HIQ8BQzqnfnmaSmGZI9PjQuSIHEIGk,329
|
|
46
|
+
prettyplay/engine/renderer/memory.py,sha256=bpMgEfh-I-XP3FpoJ-fJL4sW2IK8p69fqrtBB1Qvi58,1277
|
|
47
|
+
prettyplay/engine/renderer/render.py,sha256=Qi0TitjP_ZCJIzyYgOsL3qlq88O50pLGV2l_oTIIHlQ,6970
|
|
48
|
+
prettyplay/engine/renderer/validation.py,sha256=WhpfNvh0x4JB86q6PFpzHtbRhCM996obwaOVMUPuad4,2843
|
|
49
|
+
prettyplay/engine/renderer/.usages/memory.md,sha256=xuKr33yJIvhwGxnPWAsraTu9_jIh6W4Is7gJqt4kKE4,1203
|
|
50
|
+
prettyplay/engine/renderer/.usages/rendering.md,sha256=OsH6KJwLuwoQPT7PuNUWlqs9rNKT1qdX9rrrQOin77o,1724
|
|
51
|
+
prettyplay/engine/renderer/.usages/validation.md,sha256=Nld-ksul_XNc1aPW0y0mp10WjsgNMS2rSDOqA7ooy8k,1172
|
|
52
|
+
prettyplay/engine/steering/__init__.py,sha256=Q_aStUOjoGj6CuDXyWQTy2efQFZtnxxqqeQYPZCxPaY,150
|
|
53
|
+
prettyplay/engine/steering/steering.py,sha256=N02eXZHmr9B20N-PLENTncb6AxarItmZEjRlGYE2vdA,36115
|
|
54
|
+
prettyplay/engine/steering/.usages/steering.md,sha256=Tb9uHsvi9ZQkDLlyRVBfydNWnfsKrq3nuPQTT4qsfmA,5537
|
|
55
|
+
prettyplay/failures/__init__.py,sha256=5lyYu2fkqsIivYQt23il6Aeb09NfbZQ9kpshgJd2dwI,567
|
|
56
|
+
prettyplay/failures/errors.py,sha256=43LnujdpT0ntCUPX98qlwckz9Pdb2cUaNypoBIAP4gk,12564
|
|
57
|
+
prettyplay/failures/.usages/taxonomy.md,sha256=QLVMNXPeVhAGg8sjPXd1UHxSZRFJmmjUpTrt-KZ1B-E,7285
|
|
58
|
+
prettyplay/llm/__init__.py,sha256=WRE3T0I6UjqJncbMiIGMmfScHlOnK8Zjv1BRHDJD6SI,1039
|
|
59
|
+
prettyplay/llm/_request.py,sha256=l39sulkxzF-ulcE_vKM5XECWH9KcT-HW5gzKRIFDq3g,33313
|
|
60
|
+
prettyplay/llm/anthropic_provider.py,sha256=98rs0swMbVbAq4oRkS0OPc2QZMVV_8yfRvUQZuMSsKQ,21014
|
|
61
|
+
prettyplay/llm/models.py,sha256=cJHwIO2y7OcUVxTnCuewfmNqKpB7vZrmMw9LCKzxDOE,9818
|
|
62
|
+
prettyplay/llm/openai_provider.py,sha256=5dE4I418o3bjwywfEU9GyetomyLw7vnCqrw_HC7Kr-k,19927
|
|
63
|
+
prettyplay/llm/provider.py,sha256=d6Pql528p12zD5TGpE1e7pFbDrPjyKKe-2MdRLZKpJM,14602
|
|
64
|
+
prettyplay/llm/.usages/classification.md,sha256=sE7qSQcRzW8tV5RP7REWet3JQT2cTPdKl8W7_5w_ZIk,1801
|
|
65
|
+
prettyplay/llm/.usages/providers.md,sha256=FhmnaeYh3d7vW15_A1COtGaSETFkDPpHfimPIgseuPE,10571
|
|
66
|
+
prettyplay/llm/.usages/transport.md,sha256=N0hqRlZ6pOUJV_agGAr2aiH_lEBtnZfYcJF1Vug1nEQ,3258
|
|
67
|
+
prettyplay/reporting/__init__.py,sha256=_dmLcKn7WDDqBWNAHT0VTRHPZu2-nrMpfux60gASTU0,187
|
|
68
|
+
prettyplay/reporting/hooks.py,sha256=u7Ns6SIJ03R5rHJLDXun_pnHL1Q0oe94-V4ma98g1gI,2535
|
|
69
|
+
prettyplay/reporting/reporter.py,sha256=TlOdgCIrmUZCobxAhUzmq1qs2ycjiNMR9HOaiwfxFXM,2325
|
|
70
|
+
prettyplay/reporting/.usages/hooks.md,sha256=Vipy_4_HbPGMdwv0Kq6UQMQJfN_idQO406Ck4388LXI,4857
|
|
71
|
+
prettyplay-0.0.2.dist-info/licenses/LICENSE,sha256=3fteeL9wFLa2P6MykXx6RkApbO0N9NUrSs9rVH8gxnc,1493
|
|
72
|
+
prettyplay-0.0.2.dist-info/METADATA,sha256=ILjZ8LvRBihP0js_TEx-8WXW_4cnv0YumOKlaiJR2Pk,25440
|
|
73
|
+
prettyplay-0.0.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
74
|
+
prettyplay-0.0.2.dist-info/top_level.txt,sha256=ZByeTYDEYyWHvSGaI4cQYCrh1oiDpK5bTfnwv_ogWUg,11
|
|
75
|
+
prettyplay-0.0.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, qarium
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
prettyplay
|