Spaces:
Running
Preview 002 and 003 (512 px), version selector, Show thinker, AI-output marking
Browse files- Model selector (003 default for a first visit, last choice remembered per browser); 003 at 512 or 256 px
with its prompt pipeline (normaliser, spelled-out quoted text, count code, "no X" -> negative) and SD3 shift.
- The ONNX files now load from each release's model repo (Logolabs/agate-preview-00X, webgpu/); the graphs
also output the thinker plan. models/ (the original 001 build) stays for ?models=./models/.
- Show thinker (default on, ~2-4% per step): the 16 x 16 plan (PCA colours, as the ComfyUI live preview) and
the expected image x1 at every step.
- Every image is marked: invisible-watermark dwtDctSvd (payload AGATE + release, bit-identical JS port), PNG
provenance text chunks on save (no prompt), and a visible "AI-generated" note.
- Parity fixtures for 002 and 003, and a 38-prompt golden test of the prompt pipeline (?golden=1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018EtDckYfNgMbkwqVmTsdha
- .gitattributes +1 -0
- README.md +62 -18
- css/style.css +35 -0
- index.html +42 -10
- js/agate.js +152 -46
- js/app.js +326 -163
- js/marking.js +186 -0
- js/prompt.js +249 -0
- js/thinker.js +116 -0
- test/002/parity.json +9 -0
- test/002/parity_image.bin +3 -0
- test/002/parity_latent.bin +3 -0
- test/002/parity_noise.bin +3 -0
- test/003_256/parity.json +18 -0
- test/003_256/parity_image.bin +3 -0
- test/003_256/parity_latent.bin +3 -0
- test/003_256/parity_noise.bin +3 -0
- test/003_512/parity.json +18 -0
- test/003_512/parity_image.bin +3 -0
- test/003_512/parity_latent.bin +3 -0
- test/003_512/parity_noise.bin +3 -0
- test/prompt_golden.json +1251 -0
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.weights filter=lfs diff=lfs merge=lfs -text
|
|
@@ -12,42 +12,86 @@ custom_headers:
|
|
| 12 |
cross-origin-opener-policy: same-origin
|
| 13 |
cross-origin-resource-policy: cross-origin
|
| 14 |
models:
|
|
|
|
|
|
|
| 15 |
- Logolabs/agate-preview-001
|
| 16 |
---
|
| 17 |
|
| 18 |
# Agate WebGPU
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
|
| 24 |
**Runs entirely in your browser — prompts never leave your machine.** There is no server and no API call.
|
| 25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
## Requirements
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
- With identical initial noise, the browser output matches the PyTorch reference pipeline within 5/255
|
| 39 |
-
per pixel (run the self-test with `?parity=1`).
|
| 40 |
-
- Seeds use a JavaScript PRNG, so a seed gives the same image in every browser but not the same image as
|
| 41 |
-
the PyTorch pipeline with that seed.
|
| 42 |
|
| 43 |
-
Query parameters: `?
|
| 44 |
-
`?parity=1` runs the parity self-test
|
|
|
|
| 45 |
|
| 46 |
## Links
|
| 47 |
|
| 48 |
- [logolabs.org](https://logolabs.org)
|
| 49 |
- [huggingface.co/Logolabs](https://huggingface.co/Logolabs)
|
| 50 |
-
- Model card: [Logolabs/agate-preview-001](https://huggingface.co/Logolabs/agate-preview-001)
|
| 51 |
|
| 52 |
Licence: MIT.
|
| 53 |
|
|
|
|
| 12 |
cross-origin-opener-policy: same-origin
|
| 13 |
cross-origin-resource-policy: cross-origin
|
| 14 |
models:
|
| 15 |
+
- Logolabs/agate-preview-003
|
| 16 |
+
- Logolabs/agate-preview-002
|
| 17 |
- Logolabs/agate-preview-001
|
| 18 |
---
|
| 19 |
|
| 20 |
# Agate WebGPU
|
| 21 |
|
| 22 |
+
LogoLabs' Agate text-to-image models (a ~0.19B flow generator with a 68M text encoder) running as a static page.
|
| 23 |
+
The tokenizer, text encoder, 50-step flow sampler with classifier-free guidance and the TAESD decoder all run
|
| 24 |
+
on your own GPU through WebGPU (onnxruntime-web).
|
| 25 |
|
| 26 |
**Runs entirely in your browser — prompts never leave your machine.** There is no server and no API call.
|
| 27 |
|
| 28 |
+
## Models
|
| 29 |
+
|
| 30 |
+
Pick a model at the top of the page. A first visit gets Preview 003; the page remembers your last choice in
|
| 31 |
+
this browser.
|
| 32 |
+
|
| 33 |
+
| model | resolution | notes | download |
|
| 34 |
+
|---|---|---|---|
|
| 35 |
+
| [Preview 003](https://huggingface.co/Logolabs/agate-preview-003) | 512 px (or 256) | multi-resolution model; prompt pipeline: normaliser, quoted text spelled out, object counts coded, "no X" moved to the negative prompt; SD3 timestep shift 2 at 512 | 531 MB (both resolutions share one weights file) |
|
| 36 |
+
| [Preview 002](https://huggingface.co/Logolabs/agate-preview-002) | 256 px | same network as 001, trained longer | 530 MB |
|
| 37 |
+
| [Preview 001](https://huggingface.co/Logolabs/agate-preview-001) | 256 px | the first preview | 530 MB |
|
| 38 |
+
|
| 39 |
+
Each model is downloaded once and kept in the browser's Cache Storage. The files live in each release's model repo
|
| 40 |
+
under `webgpu/` (e.g. [Logolabs/agate-preview-003/webgpu](https://huggingface.co/Logolabs/agate-preview-003/tree/main/webgpu)),
|
| 41 |
+
with a `manifest.json` holding sizes and sha256s (the cache keys). `models/` in this Space is the original 001 build
|
| 42 |
+
(without the plan output), kept for `?models=./models/`.
|
| 43 |
+
|
| 44 |
+
## Show thinker
|
| 45 |
+
|
| 46 |
+
Agate's thinker lays the picture out on a 16 × 16 grid (the plan) that steers the renderer. With **Show thinker**
|
| 47 |
+
(on by default; it costs about 2-4% per step) the page shows, at every step, the plan of the conditional branch as
|
| 48 |
+
colours and the image the model currently expects (x₁ = z + (1 − t)·v, with the SD latent→RGB approximation) —
|
| 49 |
+
the same live preview as the [ComfyUI nodes](https://github.com/logolabs/agate-comfyui): the plan's cells are
|
| 50 |
+
projected on its top three principal components, fitted at the first step with the 2% / 98% quantiles fixed then,
|
| 51 |
+
so a colour keeps its meaning while the plan evolves.
|
| 52 |
+
|
| 53 |
## Requirements
|
| 54 |
|
| 55 |
+
A browser with WebGPU: a recent Chrome or Edge (113+). Safari and Firefox may lack WebGPU or need it enabled in
|
| 56 |
+
their settings; without it the page offers a WebAssembly (CPU) path that works but takes minutes per image.
|
| 57 |
+
|
| 58 |
+
## Speed and accuracy (RTX 4060 laptop, Edge, WebGPU, warm)
|
| 59 |
+
|
| 60 |
+
| model | per step (CFG batch of 2) | 50-step image | model setup per visit |
|
| 61 |
+
|---|---|---|---|
|
| 62 |
+
| 003 at 512 px | ~275 ms | ~14 s | ~15 s |
|
| 63 |
+
| 003 at 256 px | ~120 ms | ~6 s | ~15 s |
|
| 64 |
+
| 002 / 001 | ~105 ms | ~5.3 s | ~14 s |
|
| 65 |
+
|
| 66 |
+
With identical initial noise, the browser output matches the PyTorch packages (TAESD decoder): 001 within
|
| 67 |
+
5/255 per pixel, 002 within 14/255 (PSNR 52.8 dB), 003 within 29/255 at 512 px (PSNR 54.8 dB) and 47/255 at
|
| 68 |
+
256 px (PSNR 48.9 dB), on a few pixels only (mean difference 0.1-0.2/255). The difference comes from storing the
|
| 69 |
+
weights as fp16 to halve the download; an fp32 build matches to 1/255. Run the self-test with `?parity=1`.
|
| 70 |
+
Seeds use a JavaScript PRNG, so a seed gives the same image in every browser, but not the same image as the
|
| 71 |
+
PyTorch pipeline with that seed. The Python packages decode with the full SD-VAE by default; this page uses TAESD.
|
| 72 |
+
|
| 73 |
+
## AI-generated content marking
|
| 74 |
+
|
| 75 |
+
Every image the page makes is marked as AI-generated (EU AI Act Art. 50(2)), with the same marks as the Python
|
| 76 |
+
packages:
|
| 77 |
|
| 78 |
+
- an invisible watermark in the pixels: [invisible-watermark](https://github.com/ShieldMnt/invisible-watermark)'s
|
| 79 |
+
`dwtDctSvd` method, ported to JavaScript bit for bit, with the 64-bit payload `AGATE` + release
|
| 80 |
+
(`AGATE003`, ...). `agate.detect_watermark(img)` from the model packages reads it;
|
| 81 |
+
- in the saved PNG: text fields `ai_generated`, `generator`, `model` and `watermark`. The prompt is not written;
|
| 82 |
+
- a visible "AI-generated" note next to each result.
|
| 83 |
|
| 84 |
+
Neither mark is tamper-proof: screenshots and re-encodes drop the metadata, and heavy edits can remove the
|
| 85 |
+
watermark. If you publish images made here, label them as AI-generated.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
+
Query parameters: `?v=001|002|003` picks a model, `?res=256` picks 003's resolution, `?autoload=1` loads the
|
| 88 |
+
model immediately, `?ep=wasm` forces the CPU backend, `?parity=1` runs the parity self-test, `?golden=1` checks
|
| 89 |
+
003's prompt pipeline against the Python package's output (38 prompts).
|
| 90 |
|
| 91 |
## Links
|
| 92 |
|
| 93 |
- [logolabs.org](https://logolabs.org)
|
| 94 |
- [huggingface.co/Logolabs](https://huggingface.co/Logolabs)
|
|
|
|
| 95 |
|
| 96 |
Licence: MIT.
|
| 97 |
|
|
@@ -95,3 +95,38 @@ input[type=range] { width: 100%; margin-top: 16px; accent-color: var(--red); }
|
|
| 95 |
.mast-meta .label.dim { display: none; }
|
| 96 |
.progress-text { flex-wrap: wrap; }
|
| 97 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
.mast-meta .label.dim { display: none; }
|
| 96 |
.progress-text { flex-wrap: wrap; }
|
| 97 |
}
|
| 98 |
+
|
| 99 |
+
/* version selector, resolution, AI-generated note (2026-09-29) */
|
| 100 |
+
.versions { margin-top: 24px; }
|
| 101 |
+
.seg { display: flex; flex-wrap: wrap; gap: 0; margin-top: 8px; }
|
| 102 |
+
.seg button { font: 600 13px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; background: var(--paper); color: var(--ink);
|
| 103 |
+
border: var(--hair); margin-left: -1.5px; padding: 11px 16px; cursor: pointer; border-radius: 0; display: inline-flex; flex-direction: column; align-items: flex-start; gap: 5px; }
|
| 104 |
+
.seg button:first-child { margin-left: 0; }
|
| 105 |
+
.seg button small { font: 400 11px/1 var(--mono); letter-spacing: .06em; text-transform: none; color: var(--mute); }
|
| 106 |
+
.seg button[aria-checked="true"] { background: var(--ink); color: var(--paper); }
|
| 107 |
+
.seg button[aria-checked="true"] small { color: #c9c6bf; }
|
| 108 |
+
.seg button:hover:not([aria-checked="true"]):not(:disabled) { background: #fff; color: var(--red); }
|
| 109 |
+
.seg button:disabled { opacity: .45; cursor: default; }
|
| 110 |
+
.seg.small button { padding: 9px 12px; font-size: 12px; }
|
| 111 |
+
.ver-note { margin: 10px 0 0; max-width: 70ch; color: #2a2824; font-size: 15px; }
|
| 112 |
+
input[type=text] { width: 100%; font: 15px/1.45 var(--sans); color: var(--ink); background: #fff; border: var(--hair); border-radius: 0; padding: 8px 12px; margin: 8px 0 18px; }
|
| 113 |
+
.label .dim { color: var(--mute); text-transform: none; letter-spacing: .02em; }
|
| 114 |
+
.res-row { grid-template-columns: 1fr; }
|
| 115 |
+
.prepared { font: 12px/1.5 var(--mono); color: var(--mute); margin: 12px 0 0; overflow-wrap: anywhere; }
|
| 116 |
+
.ai-note { display: flex; gap: 10px; align-items: baseline; margin: 10px 0 0; font-size: 14px; line-height: 1.45; border-left: 3px solid var(--red); padding: 6px 0 6px 10px; background: #fff; }
|
| 117 |
+
.ai-note .sq { position: relative; top: 1px; }
|
| 118 |
+
.small-print { font-size: 13.5px; color: #2a2824; max-width: 90ch; }
|
| 119 |
+
.small-print code { font: 12.5px var(--mono); }
|
| 120 |
+
@media (max-width: 520px) {
|
| 121 |
+
#versions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
| 122 |
+
#versions button { padding: 10px 8px; font-size: 12px; letter-spacing: .06em; }
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
/* thinker preview (plan + expected image), 2026-09-29 */
|
| 126 |
+
.toggles { display: inline-flex; gap: 14px; flex-wrap: wrap; justify-content: flex-end; }
|
| 127 |
+
.thinker { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 12px; }
|
| 128 |
+
.thinker figure { margin: 0; }
|
| 129 |
+
.thinker canvas { width: 100%; aspect-ratio: 1 / 1; display: block; border: var(--hair); image-rendering: pixelated; background: #fff; }
|
| 130 |
+
.thinker #pred-canvas { image-rendering: auto; }
|
| 131 |
+
.thinker figcaption { margin-top: 6px; }
|
| 132 |
+
.thinker-note { grid-column: 1 / -1; margin: 0; font-size: 13px; color: var(--mute); }
|
|
@@ -3,8 +3,8 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="utf-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
-
<title>Agate
|
| 7 |
-
<meta name="description" content="Agate Preview 001, LogoLabs' 0.26B text-to-image
|
| 8 |
<link rel="icon" href="assets/agate-a.svg" type="image/svg+xml">
|
| 9 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 10 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
@@ -17,8 +17,8 @@
|
|
| 17 |
<div class="mast-left">
|
| 18 |
<img class="lockup" src="assets/agate-lockup.svg" alt="Agate">
|
| 19 |
<div class="mast-meta">
|
| 20 |
-
<span class="label"><i class="sq"></i>Preview
|
| 21 |
-
<span class="label dim">0.26B · text-to-image ·
|
| 22 |
</div>
|
| 23 |
</div>
|
| 24 |
<a class="imprint" href="https://logolabs.org" target="_blank" rel="noopener">
|
|
@@ -30,8 +30,14 @@
|
|
| 30 |
|
| 31 |
<section class="intro">
|
| 32 |
<h1>Agate, in your browser.</h1>
|
| 33 |
-
<p>A
|
| 34 |
-
The first visit downloads the
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
</section>
|
| 36 |
|
| 37 |
<div id="nogpu" class="notice" hidden>
|
|
@@ -39,7 +45,7 @@
|
|
| 39 |
<p id="nogpu-why"></p>
|
| 40 |
<p>Agate needs WebGPU for usable speed: use a recent Chrome or Edge (113+) on Windows, macOS or ChromeOS,
|
| 41 |
or Chrome on Android 121+; on Linux and in Firefox/Safari WebGPU may have to be enabled in the browser's flags.
|
| 42 |
-
You can still run it on the CPU (WebAssembly) — correct, but several minutes per image.</p>
|
| 43 |
<button id="use-wasm" class="btn ghost">Run on CPU instead</button>
|
| 44 |
</div>
|
| 45 |
|
|
@@ -50,6 +56,8 @@
|
|
| 50 |
<div class="examples" id="examples">
|
| 51 |
<span class="label dim">Try</span>
|
| 52 |
</div>
|
|
|
|
|
|
|
| 53 |
|
| 54 |
<div class="row">
|
| 55 |
<div class="field">
|
|
@@ -68,6 +76,12 @@
|
|
| 68 |
<input id="cfg" type="range" min="1" max="8" step="0.5" value="3">
|
| 69 |
</div>
|
| 70 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
<button id="go" class="btn primary" disabled>Load model</button>
|
| 73 |
|
|
@@ -75,19 +89,34 @@
|
|
| 75 |
<div class="bar"><div id="bar-fill"></div></div>
|
| 76 |
<div class="progress-text"><span id="status" class="label">Idle</span><span id="status-r" class="label dim"></span></div>
|
| 77 |
</div>
|
|
|
|
| 78 |
</section>
|
| 79 |
|
| 80 |
<section class="panel output">
|
| 81 |
<div class="out-head">
|
| 82 |
-
<span class="label"><i class="sq"></i>Output ·
|
| 83 |
-
<
|
|
|
|
|
|
|
|
|
|
| 84 |
</div>
|
| 85 |
<div class="frame" id="frame">
|
| 86 |
-
<canvas id="canvas" width="
|
| 87 |
<div class="placeholder" id="placeholder">
|
| 88 |
<img src="assets/agate-a.svg" alt="">
|
| 89 |
</div>
|
| 90 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
<div class="out-foot">
|
| 92 |
<dl class="timings" id="timings"></dl>
|
| 93 |
<a id="save" class="btn ghost small" download="agate.png" hidden>Save PNG</a>
|
|
@@ -100,6 +129,9 @@
|
|
| 100 |
<hr class="rule">
|
| 101 |
<footer class="foot">
|
| 102 |
<p class="strong">Runs entirely in your browser on WebGPU — nothing leaves your machine.</p>
|
|
|
|
|
|
|
|
|
|
| 103 |
<p class="label dim">
|
| 104 |
<a href="https://logolabs.org" target="_blank" rel="noopener">logolabs.org</a> ·
|
| 105 |
<a href="https://huggingface.co/Logolabs" target="_blank" rel="noopener">huggingface.co/Logolabs</a> ·
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="utf-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
+
<title>Agate — in your browser</title>
|
| 7 |
+
<meta name="description" content="Agate Preview 001, 002 and 003, LogoLabs' 0.26B text-to-image models, running entirely in your browser on WebGPU.">
|
| 8 |
<link rel="icon" href="assets/agate-a.svg" type="image/svg+xml">
|
| 9 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 10 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
|
|
| 17 |
<div class="mast-left">
|
| 18 |
<img class="lockup" src="assets/agate-lockup.svg" alt="Agate">
|
| 19 |
<div class="mast-meta">
|
| 20 |
+
<span class="label"><i class="sq"></i><span id="mast-version">Preview 003</span></span>
|
| 21 |
+
<span class="label dim" id="mast-spec">0.26B · text-to-image · 512 px</span>
|
| 22 |
</div>
|
| 23 |
</div>
|
| 24 |
<a class="imprint" href="https://logolabs.org" target="_blank" rel="noopener">
|
|
|
|
| 30 |
|
| 31 |
<section class="intro">
|
| 32 |
<h1>Agate, in your browser.</h1>
|
| 33 |
+
<p>A 0.19B-parameter flow model with a 68M text encoder, running on your own GPU through WebGPU.
|
| 34 |
+
The first visit downloads the chosen model once (<span id="dl-size">about 530 MB</span>); after that it loads from the browser cache.</p>
|
| 35 |
+
</section>
|
| 36 |
+
|
| 37 |
+
<section class="versions" aria-labelledby="ver-label">
|
| 38 |
+
<span class="label" id="ver-label">Model</span>
|
| 39 |
+
<div class="seg" role="radiogroup" aria-labelledby="ver-label" id="versions"></div>
|
| 40 |
+
<p class="ver-note" id="ver-note"></p>
|
| 41 |
</section>
|
| 42 |
|
| 43 |
<div id="nogpu" class="notice" hidden>
|
|
|
|
| 45 |
<p id="nogpu-why"></p>
|
| 46 |
<p>Agate needs WebGPU for usable speed: use a recent Chrome or Edge (113+) on Windows, macOS or ChromeOS,
|
| 47 |
or Chrome on Android 121+; on Linux and in Firefox/Safari WebGPU may have to be enabled in the browser's flags.
|
| 48 |
+
You can still run it on the CPU (WebAssembly) — correct, but several minutes per image (far longer at 512 px).</p>
|
| 49 |
<button id="use-wasm" class="btn ghost">Run on CPU instead</button>
|
| 50 |
</div>
|
| 51 |
|
|
|
|
| 56 |
<div class="examples" id="examples">
|
| 57 |
<span class="label dim">Try</span>
|
| 58 |
</div>
|
| 59 |
+
<label class="label" for="negative">Avoid <span class="dim" id="neg-hint">(optional negative prompt)</span></label>
|
| 60 |
+
<input id="negative" type="text" spellcheck="false" placeholder="e.g. text, blur">
|
| 61 |
|
| 62 |
<div class="row">
|
| 63 |
<div class="field">
|
|
|
|
| 76 |
<input id="cfg" type="range" min="1" max="8" step="0.5" value="3">
|
| 77 |
</div>
|
| 78 |
</div>
|
| 79 |
+
<div class="row res-row" id="res-row" hidden>
|
| 80 |
+
<div class="field">
|
| 81 |
+
<span class="label">Resolution</span>
|
| 82 |
+
<div class="seg small" role="radiogroup" id="res"></div>
|
| 83 |
+
</div>
|
| 84 |
+
</div>
|
| 85 |
|
| 86 |
<button id="go" class="btn primary" disabled>Load model</button>
|
| 87 |
|
|
|
|
| 89 |
<div class="bar"><div id="bar-fill"></div></div>
|
| 90 |
<div class="progress-text"><span id="status" class="label">Idle</span><span id="status-r" class="label dim"></span></div>
|
| 91 |
</div>
|
| 92 |
+
<p class="prepared" id="prepared" hidden></p>
|
| 93 |
</section>
|
| 94 |
|
| 95 |
<section class="panel output">
|
| 96 |
<div class="out-head">
|
| 97 |
+
<span class="label"><i class="sq"></i><span id="out-label">Output · 512 × 512</span></span>
|
| 98 |
+
<span class="toggles">
|
| 99 |
+
<label class="label toggle" title="Show the thinker's 16 x 16 plan and the expected final image at every step"><input type="checkbox" id="show-thinker" checked> Show thinker</label>
|
| 100 |
+
<label class="label toggle"><input type="checkbox" id="crisp"> Crisp pixels</label>
|
| 101 |
+
</span>
|
| 102 |
</div>
|
| 103 |
<div class="frame" id="frame">
|
| 104 |
+
<canvas id="canvas" width="512" height="512"></canvas>
|
| 105 |
<div class="placeholder" id="placeholder">
|
| 106 |
<img src="assets/agate-a.svg" alt="">
|
| 107 |
</div>
|
| 108 |
</div>
|
| 109 |
+
<div class="thinker" id="thinker" hidden>
|
| 110 |
+
<figure><canvas id="plan-canvas" width="16" height="16"></canvas>
|
| 111 |
+
<figcaption class="label dim">Thinker plan · 16 × 16</figcaption></figure>
|
| 112 |
+
<figure><canvas id="pred-canvas" width="32" height="32"></canvas>
|
| 113 |
+
<figcaption class="label dim">Expected · <span id="thinker-step">0 / 0</span></figcaption></figure>
|
| 114 |
+
<p class="thinker-note">The thinker lays the picture out on a 16 × 16 grid before the renderer paints it.
|
| 115 |
+
Colours are the plan's three main directions (fixed at the first step, as in the ComfyUI nodes); the
|
| 116 |
+
expected image is x₁ = z + (1 − t)·v, shown with the SD latent→RGB approximation.</p>
|
| 117 |
+
</div>
|
| 118 |
+
<p class="ai-note" id="ai-note" hidden><i class="sq red"></i><span><b>AI-generated image.</b>
|
| 119 |
+
Made by <span id="ai-model">Agate Preview 003</span>; it carries an invisible watermark and, when saved, PNG provenance metadata (no prompt).</span></p>
|
| 120 |
<div class="out-foot">
|
| 121 |
<dl class="timings" id="timings"></dl>
|
| 122 |
<a id="save" class="btn ghost small" download="agate.png" hidden>Save PNG</a>
|
|
|
|
| 129 |
<hr class="rule">
|
| 130 |
<footer class="foot">
|
| 131 |
<p class="strong">Runs entirely in your browser on WebGPU — nothing leaves your machine.</p>
|
| 132 |
+
<p class="small-print">Every image made here is marked as AI-generated (EU AI Act Art. 50): an invisible watermark in the
|
| 133 |
+
pixels (invisible-watermark “dwtDctSvd”, payload AGATE + release) and, in the saved PNG, text fields
|
| 134 |
+
<code>ai_generated</code>, <code>generator</code>, <code>model</code>. Neither survives every edit; please label AI images you publish.</p>
|
| 135 |
<p class="label dim">
|
| 136 |
<a href="https://logolabs.org" target="_blank" rel="noopener">logolabs.org</a> ·
|
| 137 |
<a href="https://huggingface.co/Logolabs" target="_blank" rel="noopener">huggingface.co/Logolabs</a> ·
|
|
@@ -1,16 +1,28 @@
|
|
| 1 |
-
// Agate
|
| 2 |
-
//
|
| 3 |
-
//
|
|
|
|
| 4 |
|
| 5 |
import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.30.0/dist/ort.webgpu.bundle.min.mjs";
|
| 6 |
import { Tokenizer } from "https://cdn.jsdelivr.net/npm/@huggingface/tokenizers@0.2.0/dist/tokenizers.mjs";
|
|
|
|
|
|
|
| 7 |
|
| 8 |
export { ort };
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
export async function webgpuStatus() {
|
| 16 |
if (!("gpu" in navigator)) return { ok: false, why: "This browser does not expose WebGPU (navigator.gpu is missing)." };
|
|
@@ -26,12 +38,12 @@ export async function webgpuStatus() {
|
|
| 26 |
}
|
| 27 |
|
| 28 |
// ---- downloads with progress + Cache Storage ---------------------------------------------------
|
| 29 |
-
async function openCache() {
|
| 30 |
-
try { return await caches.open(
|
| 31 |
}
|
| 32 |
|
| 33 |
-
async function fetchCached(url, key, expectBytes, onBytes) {
|
| 34 |
-
const cache = await openCache();
|
| 35 |
if (cache) {
|
| 36 |
const hit = await cache.match(key);
|
| 37 |
if (hit) { const buf = new Uint8Array(await hit.arrayBuffer()); onBytes(buf.byteLength, true); return { buf, cached: true }; }
|
|
@@ -56,49 +68,70 @@ async function fetchCached(url, key, expectBytes, onBytes) {
|
|
| 56 |
return { buf, cached: false };
|
| 57 |
}
|
| 58 |
|
| 59 |
-
export async function hasCachedModel() {
|
| 60 |
-
try { const c = await caches.open(
|
| 61 |
}
|
| 62 |
|
| 63 |
-
export async function clearCache() {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
// The Hub counts a model download only on a request for the model repo's root config.json; the ONNX files
|
| 66 |
-
// come from this Space, so every model load reads
|
| 67 |
-
// from_pretrained() produces, cached weights or not.
|
| 68 |
-
function countLoad() { fetch(
|
| 69 |
|
| 70 |
// ---- the model ---------------------------------------------------------------------------------
|
| 71 |
export class Agate {
|
| 72 |
-
|
|
|
|
| 73 |
this.optLevel = optLevel;
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
| 75 |
this.ep = ep;
|
| 76 |
-
this.variant = variant; // e.g. "full16": fp16-compute generator (experimental)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
}
|
| 78 |
|
| 79 |
-
// onProgress({loaded, total, file, phase})
|
| 80 |
-
async load(onProgress = () => {}) {
|
| 81 |
const manifest = await (await fetch(this.base + "manifest.json", { cache: "no-cache" })).json();
|
| 82 |
this.manifest = manifest;
|
| 83 |
const swap = (this.variant && manifest.variants?.[this.variant]) || {};
|
| 84 |
const real = (f) => swap[f] || f;
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
let loaded = 0, fromCache = 0;
|
| 87 |
const bufs = {};
|
| 88 |
const t0 = performance.now();
|
| 89 |
-
for (const f of
|
| 90 |
const meta = manifest.files[real(f)];
|
| 91 |
-
const
|
| 92 |
-
const { buf, cached } = await fetchCached(this.base + real(f), key, meta.bytes, (b, c) => {
|
| 93 |
loaded += b; if (c) fromCache += b; onProgress({ phase: "download", file: f, loaded, total });
|
| 94 |
});
|
| 95 |
bufs[f] = buf;
|
| 96 |
}
|
| 97 |
this.stats = { downloadMB: total / 1e6, fromCacheMB: fromCache / 1e6, downloadMs: performance.now() - t0 };
|
| 98 |
-
countLoad();
|
| 99 |
-
// drop cached files of older
|
| 100 |
try {
|
| 101 |
-
const cache = await openCache(), keep = new Set(
|
| 102 |
if (cache) for (const req of await cache.keys()) if (!keep.has(req.url)) await cache.delete(req);
|
| 103 |
} catch (e) { console.warn("cache cleanup", e); }
|
| 104 |
const dec = new TextDecoder();
|
|
@@ -107,20 +140,53 @@ export class Agate {
|
|
| 107 |
if (this.ep === "wasm") {
|
| 108 |
ort.env.wasm.numThreads = self.crossOriginIsolated ? Math.min(8, navigator.hardwareConcurrency || 4) : 1;
|
| 109 |
}
|
| 110 |
-
const opts = { executionProviders: [this.ep], graphOptimizationLevel: this.optLevel };
|
| 111 |
const t1 = performance.now();
|
| 112 |
this.sessions = {};
|
| 113 |
-
for (const [name, f] of [["text", "text_encoder.onnx"], ["
|
| 114 |
onProgress({ phase: "init", file: f, loaded: total, total });
|
| 115 |
const ts = performance.now();
|
| 116 |
-
this.sessions[name] = await ort.InferenceSession.create(bufs[f],
|
| 117 |
this.stats[`${name}SessionMs`] = performance.now() - ts;
|
| 118 |
bufs[f] = null;
|
| 119 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
this.stats.sessionMs = performance.now() - t1;
|
| 121 |
return this.stats;
|
| 122 |
}
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
tokenize(text) {
|
| 125 |
const max = this.manifest.text_max_len;
|
| 126 |
let ids = this.tok.encode(text).ids; // [CLS] ... [SEP], as the HF tokenizer
|
|
@@ -139,7 +205,15 @@ export class Agate {
|
|
| 139 |
const h = out.last_hidden_state;
|
| 140 |
const data = h.data.slice(); // (1, n, 512) fp32
|
| 141 |
h.dispose?.();
|
| 142 |
-
return { data, n };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
}
|
| 144 |
|
| 145 |
static gaussianNoise(seed, count) {
|
|
@@ -156,38 +230,67 @@ export class Agate {
|
|
| 156 |
return out;
|
| 157 |
}
|
| 158 |
|
| 159 |
-
//
|
| 160 |
-
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
const per = C * hw * hw;
|
| 163 |
const T = { };
|
| 164 |
let t0 = performance.now();
|
| 165 |
-
const
|
|
|
|
| 166 |
T.textMs = performance.now() - t0;
|
|
|
|
| 167 |
const need = Math.max(c.n, u.n);
|
| 168 |
const L = BUCKETS.find((b) => b >= need) ?? BUCKETS[BUCKETS.length - 1];
|
| 169 |
const ctx = new Float32Array(2 * L * D), mask = new Float32Array(2 * L);
|
| 170 |
ctx.set(c.data, 0); ctx.set(u.data, L * D); // zero padding, as F.pad in the pipeline
|
| 171 |
mask.fill(1, 0, c.n); mask.fill(1, L, L + u.n);
|
| 172 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
let z = noise ? Float32Array.from(noise) : Agate.gaussianNoise(seed, per);
|
| 175 |
const zz = new Float32Array(2 * per), tt = new Float32Array(2);
|
| 176 |
-
const
|
| 177 |
t0 = performance.now();
|
| 178 |
const stepMs = [];
|
| 179 |
for (let i = 0; i < steps; i++) {
|
| 180 |
if (shouldStop()) throw new Error("stopped");
|
| 181 |
const ts = performance.now();
|
| 182 |
zz.set(z, 0); zz.set(z, per);
|
| 183 |
-
tt[0] = tt[1] = i
|
| 184 |
-
const
|
| 185 |
-
|
| 186 |
-
});
|
| 187 |
const v = out.v.data;
|
|
|
|
| 188 |
const zn = new Float32Array(per);
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
out.v.dispose?.();
|
|
|
|
| 191 |
z = zn;
|
| 192 |
stepMs.push(performance.now() - ts);
|
| 193 |
onStep(i + 1, steps, z);
|
|
@@ -209,8 +312,11 @@ export class Agate {
|
|
| 209 |
rgba[p * 4 + 3] = 255;
|
| 210 |
}
|
| 211 |
T.decodeMs = performance.now() - t0;
|
| 212 |
-
|
|
|
|
|
|
|
|
|
|
| 213 |
T.bucket = L;
|
| 214 |
-
return { rgba, width: W, height: H, latent: z, timings: T };
|
| 215 |
}
|
| 216 |
}
|
|
|
|
| 1 |
+
// Agate in the browser: tokenizer (tokenizers.js) + ONNX graphs run by onnxruntime-web (WebGPU, WASM
|
| 2 |
+
// fallback) + an Euler flow sampler with CFG. Mirrors agate/pipeline.py (AgatePipeline) of
|
| 3 |
+
// Logolabs/agate-preview-001 / -002 (fcdm_t2, 256 px) and Logolabs/agate-preview-003 (fcdm_t2mr: 512 or 256 px,
|
| 4 |
+
// SD3 timestep shift, prompt pipeline + count code, see prompt.js). Every image is marked (marking.js).
|
| 5 |
|
| 6 |
import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.30.0/dist/ort.webgpu.bundle.min.mjs";
|
| 7 |
import { Tokenizer } from "https://cdn.jsdelivr.net/npm/@huggingface/tokenizers@0.2.0/dist/tokenizers.mjs";
|
| 8 |
+
import { prepare, countVector } from "./prompt.js";
|
| 9 |
+
import { embedWatermark, marks } from "./marking.js";
|
| 10 |
|
| 11 |
export { ort };
|
| 12 |
|
| 13 |
+
// The releases this page runs. remote: where the files live -- each release's model repo, under webgpu/ (the
|
| 14 |
+
// Space repo has a 1 GB storage limit; the graphs there also output the thinker plan). The Space's old models/
|
| 15 |
+
// (001 without the plan output) is used only through ?models=. dir: the layout under a local
|
| 16 |
+
// models base (?models=<url> or window.AGATE_MODEL_BASE given explicitly, e.g. for local testing). cache: Cache
|
| 17 |
+
// Storage name (001 keeps the name it always had, so returning visitors do not download it again).
|
| 18 |
+
const HUB = (v) => `https://huggingface.co/Logolabs/agate-preview-${v}/resolve/main/webgpu/`;
|
| 19 |
+
export const VERSIONS = {
|
| 20 |
+
"003": { remote: HUB("003"), dir: "003/", cache: "agate-preview-003-web", res: [512, 256], note: "Multi-resolution model, 512 px (or 256). Quoted text is spelled out, counts are coded, “no X” becomes a negative prompt." },
|
| 21 |
+
"002": { remote: HUB("002"), dir: "002/", cache: "agate-preview-002-web", res: [256], note: "Same network as 001, trained 38,710 steps longer before the same anneal. 256 px, plain prompts." },
|
| 22 |
+
"001": { remote: HUB("001"), dir: "", cache: "agate-preview-001-web", res: [256], note: "The first preview (2026-09-25). 256 px, plain prompts." },
|
| 23 |
+
};
|
| 24 |
+
const countUrl = (v) => `https://huggingface.co/Logolabs/agate-preview-${v}/resolve/main/config.json`;
|
| 25 |
+
const FILES_T2 = ["tokenizer.json", "tokenizer_config.json", "text_encoder.onnx", "generator.onnx", "taesd_decoder.onnx"];
|
| 26 |
|
| 27 |
export async function webgpuStatus() {
|
| 28 |
if (!("gpu" in navigator)) return { ok: false, why: "This browser does not expose WebGPU (navigator.gpu is missing)." };
|
|
|
|
| 38 |
}
|
| 39 |
|
| 40 |
// ---- downloads with progress + Cache Storage ---------------------------------------------------
|
| 41 |
+
async function openCache(name) {
|
| 42 |
+
try { return await caches.open(name); } catch { return null; } // file://, private mode, ...
|
| 43 |
}
|
| 44 |
|
| 45 |
+
async function fetchCached(cacheName, url, key, expectBytes, onBytes) {
|
| 46 |
+
const cache = await openCache(cacheName);
|
| 47 |
if (cache) {
|
| 48 |
const hit = await cache.match(key);
|
| 49 |
if (hit) { const buf = new Uint8Array(await hit.arrayBuffer()); onBytes(buf.byteLength, true); return { buf, cached: true }; }
|
|
|
|
| 68 |
return { buf, cached: false };
|
| 69 |
}
|
| 70 |
|
| 71 |
+
export async function hasCachedModel(version = "001") {
|
| 72 |
+
try { const c = await caches.open(VERSIONS[version].cache); return (await c.keys()).length >= 5; } catch { return false; }
|
| 73 |
}
|
| 74 |
|
| 75 |
+
export async function clearCache() {
|
| 76 |
+
let ok = true;
|
| 77 |
+
for (const v of Object.values(VERSIONS)) { try { ok = (await caches.delete(v.cache)) && ok; } catch { ok = false; } }
|
| 78 |
+
return ok;
|
| 79 |
+
}
|
| 80 |
|
| 81 |
// The Hub counts a model download only on a request for the model repo's root config.json; the ONNX files
|
| 82 |
+
// come from this Space, so every model load reads the loaded release's config.json once -- the same per-load
|
| 83 |
+
// count a Python from_pretrained() produces, cached weights or not.
|
| 84 |
+
function countLoad(version) { fetch(countUrl(version), { cache: "no-store" }).catch(() => {}); }
|
| 85 |
|
| 86 |
// ---- the model ---------------------------------------------------------------------------------
|
| 87 |
export class Agate {
|
| 88 |
+
// base: the page's models folder (001); override: an explicit models base for every version (local layout)
|
| 89 |
+
constructor({ base = "./models/", override = null, version = "003", ep = "webgpu", variant = null, optLevel = "all" } = {}) {
|
| 90 |
this.optLevel = optLevel;
|
| 91 |
+
const slash = (u) => (u.endsWith("/") ? u : u + "/");
|
| 92 |
+
this.version = version;
|
| 93 |
+
this.spec = VERSIONS[version];
|
| 94 |
+
this.base = override ? slash(override) + this.spec.dir : (this.spec.remote || slash(base) + this.spec.dir);
|
| 95 |
this.ep = ep;
|
| 96 |
+
this.variant = variant; // e.g. "full16": fp16-compute generator (experimental, 001 only)
|
| 97 |
+
this.marks = marks(version);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
get mr() { return this.manifest?.arch === "fcdm_t2mr"; }
|
| 101 |
+
|
| 102 |
+
fileKey(f) { return new Request(`${this.base}${f}?sha256=${this.manifest.files[f].sha256}`); }
|
| 103 |
+
|
| 104 |
+
async getFile(f, onBytes = () => {}) {
|
| 105 |
+
const meta = this.manifest.files[f];
|
| 106 |
+
return (await fetchCached(this.spec.cache, this.base + f, this.fileKey(f), meta.bytes, onBytes)).buf;
|
| 107 |
}
|
| 108 |
|
| 109 |
+
// onProgress({loaded, total, file, phase}); resolution: 003 only (512 default, or 256)
|
| 110 |
+
async load(onProgress = () => {}, resolution = null) {
|
| 111 |
const manifest = await (await fetch(this.base + "manifest.json", { cache: "no-cache" })).json();
|
| 112 |
this.manifest = manifest;
|
| 113 |
const swap = (this.variant && manifest.variants?.[this.variant]) || {};
|
| 114 |
const real = (f) => swap[f] || f;
|
| 115 |
+
this.resolution = this.mr ? Number(resolution || manifest.resolution) : 256;
|
| 116 |
+
// 003: download everything (the two graphs are ~1 MB each and share one weights file), so a resolution
|
| 117 |
+
// switch needs no network
|
| 118 |
+
const need = this.mr ? Object.keys(manifest.files) : FILES_T2;
|
| 119 |
+
const total = need.reduce((s, f) => s + manifest.files[real(f)].bytes, 0);
|
| 120 |
let loaded = 0, fromCache = 0;
|
| 121 |
const bufs = {};
|
| 122 |
const t0 = performance.now();
|
| 123 |
+
for (const f of need) {
|
| 124 |
const meta = manifest.files[real(f)];
|
| 125 |
+
const { buf } = await fetchCached(this.spec.cache, this.base + real(f), this.fileKey(real(f)), meta.bytes, (b, c) => {
|
|
|
|
| 126 |
loaded += b; if (c) fromCache += b; onProgress({ phase: "download", file: f, loaded, total });
|
| 127 |
});
|
| 128 |
bufs[f] = buf;
|
| 129 |
}
|
| 130 |
this.stats = { downloadMB: total / 1e6, fromCacheMB: fromCache / 1e6, downloadMs: performance.now() - t0 };
|
| 131 |
+
countLoad(this.version);
|
| 132 |
+
// drop cached files of older builds of this release (keys carry the sha256)
|
| 133 |
try {
|
| 134 |
+
const cache = await openCache(this.spec.cache), keep = new Set(need.map((f) => new URL(this.fileKey(real(f)).url, location.href).href));
|
| 135 |
if (cache) for (const req of await cache.keys()) if (!keep.has(req.url)) await cache.delete(req);
|
| 136 |
} catch (e) { console.warn("cache cleanup", e); }
|
| 137 |
const dec = new TextDecoder();
|
|
|
|
| 140 |
if (this.ep === "wasm") {
|
| 141 |
ort.env.wasm.numThreads = self.crossOriginIsolated ? Math.min(8, navigator.hardwareConcurrency || 4) : 1;
|
| 142 |
}
|
|
|
|
| 143 |
const t1 = performance.now();
|
| 144 |
this.sessions = {};
|
| 145 |
+
for (const [name, f] of [["text", "text_encoder.onnx"], ["vae", "taesd_decoder.onnx"]]) {
|
| 146 |
onProgress({ phase: "init", file: f, loaded: total, total });
|
| 147 |
const ts = performance.now();
|
| 148 |
+
this.sessions[name] = await ort.InferenceSession.create(bufs[f], this.sessionOptions());
|
| 149 |
this.stats[`${name}SessionMs`] = performance.now() - ts;
|
| 150 |
bufs[f] = null;
|
| 151 |
}
|
| 152 |
+
onProgress({ phase: "init", file: "generator", loaded: total, total });
|
| 153 |
+
const ts = performance.now();
|
| 154 |
+
if (this.mr) {
|
| 155 |
+
this.weights = bufs["generator.weights"]; // kept for a resolution switch (no re-read)
|
| 156 |
+
await this.createGenerator(this.resolution, bufs);
|
| 157 |
+
} else {
|
| 158 |
+
this.sessions.gen = await ort.InferenceSession.create(bufs["generator.onnx"], this.sessionOptions());
|
| 159 |
+
}
|
| 160 |
+
this.stats.genSessionMs = performance.now() - ts;
|
| 161 |
this.stats.sessionMs = performance.now() - t1;
|
| 162 |
return this.stats;
|
| 163 |
}
|
| 164 |
|
| 165 |
+
sessionOptions(extra = {}) { return { executionProviders: [this.ep], graphOptimizationLevel: this.optLevel, ...extra }; }
|
| 166 |
+
|
| 167 |
+
// 003: the generator graph for one resolution; the weights file is shared by both graphs.
|
| 168 |
+
async createGenerator(res, bufs = {}) {
|
| 169 |
+
const r = this.manifest.resolutions[String(res)];
|
| 170 |
+
if (!r) throw new Error(`resolution ${res} not available`);
|
| 171 |
+
const graph = bufs[r.graph] || await this.getFile(r.graph);
|
| 172 |
+
const weights = this.weights || await this.getFile(r.external_data);
|
| 173 |
+
if (this.sessions.gen) { try { await this.sessions.gen.release(); } catch { /* */ } this.sessions.gen = null; }
|
| 174 |
+
this.sessions.gen = await ort.InferenceSession.create(graph, this.sessionOptions({ externalData: [{ path: r.external_data, data: weights }] }));
|
| 175 |
+
this.resolution = Number(res);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
async setResolution(res) {
|
| 179 |
+
if (!this.mr || Number(res) === this.resolution) return 0;
|
| 180 |
+
const t0 = performance.now();
|
| 181 |
+
await this.createGenerator(res);
|
| 182 |
+
return performance.now() - t0;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
async release() {
|
| 186 |
+
for (const s of Object.values(this.sessions || {})) { try { await s?.release(); } catch { /* */ } }
|
| 187 |
+
this.sessions = null; this.weights = null;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
tokenize(text) {
|
| 191 |
const max = this.manifest.text_max_len;
|
| 192 |
let ids = this.tok.encode(text).ids; // [CLS] ... [SEP], as the HF tokenizer
|
|
|
|
| 205 |
const h = out.last_hidden_state;
|
| 206 |
const data = h.data.slice(); // (1, n, 512) fp32
|
| 207 |
h.dispose?.();
|
| 208 |
+
return { data, n, ids };
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
// The prompt as the model sees it: 003 runs its prompt pipeline (prompt.js), 001/002 use the prompt as typed.
|
| 212 |
+
prepare(prompt, negative = "") {
|
| 213 |
+
const pp = this.manifest.prompt_pipeline;
|
| 214 |
+
if (!pp) return { text: prompt, negative };
|
| 215 |
+
const [text, neg] = prepare(prompt, negative, pp.normalize, pp.spell);
|
| 216 |
+
return { text, negative: neg };
|
| 217 |
}
|
| 218 |
|
| 219 |
static gaussianNoise(seed, count) {
|
|
|
|
| 230 |
return out;
|
| 231 |
}
|
| 232 |
|
| 233 |
+
// SD3's resolution shift in Agate's convention (t = 0 noise), as agate/pipeline.py shift_t
|
| 234 |
+
static shiftT(t, shift) {
|
| 235 |
+
if (shift === 1) return t;
|
| 236 |
+
const s = 1 - t;
|
| 237 |
+
return 1 - shift * s / (1 + (shift - 1) * s);
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
// -> { rgba (watermarked unless watermark=false), width, height, latent, timings, prepared }
|
| 241 |
+
get hasPlan() { return !!this.sessions?.gen?.outputNames?.includes("plan"); }
|
| 242 |
+
|
| 243 |
+
// onPreview({ step, steps, plan, x1, hw }) after every step, when given and the graph outputs the plan: plan =
|
| 244 |
+
// the thinker output for the conditional branch (Float32Array 640 x 16 x 16), x1 = z + (1 - t) v (guided).
|
| 245 |
+
async generate({ prompt, negative = "", seed = 0, steps = 50, cfg = 3.0, noise = null, watermark = true, onStep = () => {}, onPreview = null, shouldStop = () => false }) {
|
| 246 |
+
const C = this.manifest.latent_ch, D = this.manifest.ctx_dim;
|
| 247 |
+
const rc = this.mr ? this.manifest.resolutions[String(this.resolution)] : { latent_hw: this.manifest.latent_hw, shift: 1 };
|
| 248 |
+
const hw = rc.latent_hw, shift = Number(rc.shift || 1);
|
| 249 |
const per = C * hw * hw;
|
| 250 |
const T = { };
|
| 251 |
let t0 = performance.now();
|
| 252 |
+
const prep = this.prepare(prompt, negative);
|
| 253 |
+
const c = await this.encode(prep.text), u = await this.encode(prep.negative);
|
| 254 |
T.textMs = performance.now() - t0;
|
| 255 |
+
const BUCKETS = this.manifest.buckets;
|
| 256 |
const need = Math.max(c.n, u.n);
|
| 257 |
const L = BUCKETS.find((b) => b >= need) ?? BUCKETS[BUCKETS.length - 1];
|
| 258 |
const ctx = new Float32Array(2 * L * D), mask = new Float32Array(2 * L);
|
| 259 |
ctx.set(c.data, 0); ctx.set(u.data, L * D); // zero padding, as F.pad in the pipeline
|
| 260 |
mask.fill(1, 0, c.n); mask.fill(1, L, L + u.n);
|
| 261 |
+
const feeds = { ctx: new ort.Tensor("float32", ctx, [2, L, D]), mask: new ort.Tensor("float32", mask, [2, L]) };
|
| 262 |
+
let countsNonzero = 0;
|
| 263 |
+
if (this.mr) { // count code: conditional half only
|
| 264 |
+
const cnt = new Float32Array(2 * L);
|
| 265 |
+
if (this.manifest.prompt_pipeline?.count_code) cnt.set(countVector(this.tok, prep.text, c.ids, Math.min(L, c.n)), 0);
|
| 266 |
+
countsNonzero = cnt.reduce((s, v) => s + (v > 0), 0);
|
| 267 |
+
feeds.counts = new ort.Tensor("float32", cnt, [2, L]);
|
| 268 |
+
}
|
| 269 |
|
| 270 |
let z = noise ? Float32Array.from(noise) : Agate.gaussianNoise(seed, per);
|
| 271 |
const zz = new Float32Array(2 * per), tt = new Float32Array(2);
|
| 272 |
+
const grid = Array.from({ length: steps + 1 }, (_, i) => Agate.shiftT(i / steps, shift));
|
| 273 |
t0 = performance.now();
|
| 274 |
const stepMs = [];
|
| 275 |
for (let i = 0; i < steps; i++) {
|
| 276 |
if (shouldStop()) throw new Error("stopped");
|
| 277 |
const ts = performance.now();
|
| 278 |
zz.set(z, 0); zz.set(z, per);
|
| 279 |
+
tt[0] = tt[1] = grid[i];
|
| 280 |
+
const want = onPreview && this.hasPlan ? ["v", "plan"] : ["v"];
|
| 281 |
+
const out = await this.sessions.gen.run({ z: new ort.Tensor("float32", zz, [2, C, hw, hw]), t: new ort.Tensor("float32", tt, [2]), ...feeds }, want);
|
|
|
|
| 282 |
const v = out.v.data;
|
| 283 |
+
const dt = grid[i + 1] - grid[i];
|
| 284 |
const zn = new Float32Array(per);
|
| 285 |
+
let x1 = null;
|
| 286 |
+
if (out.plan) x1 = new Float32Array(per);
|
| 287 |
+
for (let k = 0; k < per; k++) {
|
| 288 |
+
const vc = v[k], vu = v[per + k], g = vu + cfg * (vc - vu);
|
| 289 |
+
zn[k] = z[k] + dt * g;
|
| 290 |
+
if (x1) x1[k] = z[k] + (1 - grid[i]) * g;
|
| 291 |
+
}
|
| 292 |
out.v.dispose?.();
|
| 293 |
+
if (out.plan) { const plan = out.plan.data.slice(); out.plan.dispose?.(); onPreview({ step: i + 1, steps, plan, x1, hw }); }
|
| 294 |
z = zn;
|
| 295 |
stepMs.push(performance.now() - ts);
|
| 296 |
onStep(i + 1, steps, z);
|
|
|
|
| 312 |
rgba[p * 4 + 3] = 255;
|
| 313 |
}
|
| 314 |
T.decodeMs = performance.now() - t0;
|
| 315 |
+
t0 = performance.now();
|
| 316 |
+
if (watermark) embedWatermark(rgba, W, H, this.marks.payload);
|
| 317 |
+
T.markMs = performance.now() - t0;
|
| 318 |
+
T.totalMs = T.textMs + T.samplerMs + T.decodeMs + T.markMs;
|
| 319 |
T.bucket = L;
|
| 320 |
+
return { rgba, width: W, height: H, latent: z, timings: T, prepared: { ...prep, countsNonzero }, watermarked: watermark };
|
| 321 |
}
|
| 322 |
}
|
|
@@ -1,163 +1,326 @@
|
|
| 1 |
-
import { Agate, webgpuStatus, clearCache, hasCachedModel } from "./agate.js";
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
const
|
| 9 |
-
|
| 10 |
-
const
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
const
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
const
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
ui.
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
ui.
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
}
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Agate, VERSIONS, webgpuStatus, clearCache, hasCachedModel } from "./agate.js";
|
| 2 |
+
import { addPngText, readWatermark } from "./marking.js";
|
| 3 |
+
import { prepare as preparePrompt, countVector } from "./prompt.js";
|
| 4 |
+
import { ThinkerView } from "./thinker.js";
|
| 5 |
+
|
| 6 |
+
// Model location: 001 from window.AGATE_MODEL_BASE (js/config.js, default ./models/), 002 / 003 from their model
|
| 7 |
+
// repos (agate.js VERSIONS); ?models=<url> serves every version from one local-layout base instead.
|
| 8 |
+
const params = new URLSearchParams(location.search);
|
| 9 |
+
const BASE = window.AGATE_MODEL_BASE || "./models/"; // 001's files (002 / 003 come from their model repos)
|
| 10 |
+
const MODELS_OVERRIDE = params.get("models"); // every version from <url>/{,002/,003/} (local testing)
|
| 11 |
+
const FORCE_EP = params.get("ep"); // "wasm" forces the CPU path
|
| 12 |
+
const PARITY = params.has("parity");
|
| 13 |
+
const GOLDEN = params.has("golden"); // prompt-pipeline golden test (003)
|
| 14 |
+
const VARIANT = params.get("variant"); // "full16": experimental fp16-compute generator (001)
|
| 15 |
+
|
| 16 |
+
const store = {
|
| 17 |
+
get(k) { try { return localStorage.getItem(k); } catch { return null; } },
|
| 18 |
+
set(k, v) { try { localStorage.setItem(k, v); } catch { /* storage blocked */ } },
|
| 19 |
+
};
|
| 20 |
+
const DEFAULT_VERSION = "003";
|
| 21 |
+
let version = [params.get("v"), store.get("agate-version"), DEFAULT_VERSION].find((v) => v && VERSIONS[v]);
|
| 22 |
+
let resolution = Number(params.get("res") || store.get("agate-res-003") || 512);
|
| 23 |
+
if (!VERSIONS["003"].res.includes(resolution)) resolution = 512;
|
| 24 |
+
|
| 25 |
+
const EXAMPLES = {
|
| 26 |
+
common: [
|
| 27 |
+
"a green teapot and a red cup on a table",
|
| 28 |
+
"a minimalist logo of a fox head, orange, flat design, white background",
|
| 29 |
+
"a dog sitting to the left of a cat",
|
| 30 |
+
"a red cube on top of a blue sphere",
|
| 31 |
+
],
|
| 32 |
+
"003": [
|
| 33 |
+
'a shop sign that says "OPEN", three red apples, no people',
|
| 34 |
+
"a portrait of an old fisherman at golden hour, detailed",
|
| 35 |
+
'a coffee cup with the text "Good Morning" on it',
|
| 36 |
+
],
|
| 37 |
+
};
|
| 38 |
+
|
| 39 |
+
const $ = (id) => document.getElementById(id);
|
| 40 |
+
const ui = {
|
| 41 |
+
prompt: $("prompt"), negative: $("negative"), seed: $("seed"), steps: $("steps"), cfg: $("cfg"), go: $("go"), dice: $("dice"),
|
| 42 |
+
fill: $("bar-fill"), status: $("status"), statusR: $("status-r"), canvas: $("canvas"), frame: $("frame"),
|
| 43 |
+
placeholder: $("placeholder"), timings: $("timings"), save: $("save"), crisp: $("crisp"), backend: $("backend"),
|
| 44 |
+
versions: $("versions"), res: $("res"), resRow: $("res-row"), prepared: $("prepared"), aiNote: $("ai-note"),
|
| 45 |
+
showThinker: $("show-thinker"), thinker: $("thinker"), thinkerStep: $("thinker-step"),
|
| 46 |
+
};
|
| 47 |
+
const thinkerView = new ThinkerView($("plan-canvas"), $("pred-canvas"));
|
| 48 |
+
ui.showThinker.checked = store.get("agate-thinker") !== "0";
|
| 49 |
+
ui.showThinker.onchange = () => { store.set("agate-thinker", ui.showThinker.checked ? "1" : "0"); if (!ui.showThinker.checked) ui.thinker.hidden = true; };
|
| 50 |
+
|
| 51 |
+
let agate = null, ep = FORCE_EP || "webgpu", busy = false, stop = false, gpuOk = true, last = null;
|
| 52 |
+
window.__agate = { state: "idle", version }; // read by the automated browser test
|
| 53 |
+
|
| 54 |
+
const res = () => (version === "003" ? resolution : 256);
|
| 55 |
+
|
| 56 |
+
function renderExamples() {
|
| 57 |
+
const box = $("examples");
|
| 58 |
+
box.querySelectorAll(".chip").forEach((c) => c.remove());
|
| 59 |
+
for (const p of [...(EXAMPLES[version] || []), ...EXAMPLES.common].slice(0, 6)) {
|
| 60 |
+
const b = document.createElement("button");
|
| 61 |
+
b.className = "chip"; b.textContent = p; b.type = "button";
|
| 62 |
+
b.onclick = () => { ui.prompt.value = p; };
|
| 63 |
+
box.appendChild(b);
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
function seg(el, items, current, onPick) {
|
| 68 |
+
el.innerHTML = "";
|
| 69 |
+
for (const it of items) {
|
| 70 |
+
const b = document.createElement("button");
|
| 71 |
+
b.type = "button"; b.setAttribute("role", "radio"); b.setAttribute("aria-checked", String(it.value === current));
|
| 72 |
+
b.dataset.value = it.value;
|
| 73 |
+
b.innerHTML = `${it.label}${it.sub ? `<small>${it.sub}</small>` : ""}`;
|
| 74 |
+
b.onclick = () => { if (!busy && it.value !== current) onPick(it.value); };
|
| 75 |
+
el.appendChild(b);
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
function renderVersion() {
|
| 80 |
+
const spec = VERSIONS[version];
|
| 81 |
+
seg(ui.versions, Object.keys(VERSIONS).map((v) => ({ value: v, label: `Preview ${v}`, sub: `${Math.max(...VERSIONS[v].res)} px${v === DEFAULT_VERSION ? " · newest" : ""}` })),
|
| 82 |
+
version, pickVersion);
|
| 83 |
+
$("ver-note").textContent = spec.note;
|
| 84 |
+
$("mast-version").textContent = `Preview ${version}`;
|
| 85 |
+
$("mast-spec").textContent = `0.26B · text-to-image · ${spec.res.join(" / ")} px`;
|
| 86 |
+
$("ai-model").textContent = `Agate Preview ${version}`;
|
| 87 |
+
$("neg-hint").textContent = version === "003" ? "(optional; “no X” in the prompt is added automatically)" : "(optional negative prompt)";
|
| 88 |
+
ui.resRow.hidden = spec.res.length < 2;
|
| 89 |
+
if (spec.res.length > 1) seg(ui.res, spec.res.map((r) => ({ value: r, label: `${r} × ${r}`, sub: r === 512 ? "native" : "faster" })), resolution, pickRes);
|
| 90 |
+
$("out-label").textContent = `Output · ${res()} × ${res()}`;
|
| 91 |
+
renderExamples();
|
| 92 |
+
document.title = `Agate Preview ${version} — in your browser`;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
async function pickVersion(v) {
|
| 96 |
+
version = v; store.set("agate-version", v);
|
| 97 |
+
window.__agate.version = v;
|
| 98 |
+
if (agate) { await agate.release(); agate = null; }
|
| 99 |
+
clearOutput();
|
| 100 |
+
renderVersion();
|
| 101 |
+
if (!gpuOk && !FORCE_EP) return;
|
| 102 |
+
ui.go.textContent = "Load model"; ui.go.disabled = false;
|
| 103 |
+
setProgress(0, "Idle", "");
|
| 104 |
+
if (await hasCachedModel(v)) await load();
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
async function pickRes(r) {
|
| 108 |
+
resolution = r; store.set("agate-res-003", String(r));
|
| 109 |
+
renderVersion();
|
| 110 |
+
if (agate?.sessions && agate.mr) {
|
| 111 |
+
busy = true; ui.go.disabled = true;
|
| 112 |
+
setProgress(1, `Preparing the ${r} px generator`, "");
|
| 113 |
+
try { const ms = await agate.setResolution(r); setProgress(0, "Ready", `${r} px · ${(ms / 1000).toFixed(1)} s`); }
|
| 114 |
+
catch (e) { console.error(e); setProgress(0, "Switch failed", String(e.message || e)); }
|
| 115 |
+
busy = false; ui.go.disabled = false;
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
ui.steps.oninput = () => { $("steps-v").textContent = ui.steps.value; };
|
| 120 |
+
ui.cfg.oninput = () => { $("cfg-v").textContent = Number(ui.cfg.value).toFixed(1); };
|
| 121 |
+
ui.dice.onclick = () => { ui.seed.value = Math.floor(Math.random() * 2 ** 31); };
|
| 122 |
+
ui.crisp.checked = store.get("agate-crisp") === "1";
|
| 123 |
+
const applyCrisp = () => { ui.frame.classList.toggle("crisp", ui.crisp.checked); store.set("agate-crisp", ui.crisp.checked ? "1" : "0"); };
|
| 124 |
+
ui.crisp.onchange = applyCrisp; applyCrisp();
|
| 125 |
+
|
| 126 |
+
const mb = (b) => (b / 1e6).toFixed(0);
|
| 127 |
+
function setProgress(frac, left, right = "") {
|
| 128 |
+
ui.fill.style.width = `${Math.max(0, Math.min(1, frac)) * 100}%`;
|
| 129 |
+
ui.status.textContent = left; ui.statusR.textContent = right;
|
| 130 |
+
}
|
| 131 |
+
function showTimings(rows) {
|
| 132 |
+
ui.timings.innerHTML = rows.map(([k, v]) => `<dt>${k}</dt><dd>${v}</dd>`).join("");
|
| 133 |
+
}
|
| 134 |
+
function clearOutput() {
|
| 135 |
+
ui.placeholder.hidden = false; ui.aiNote.hidden = true; ui.thinker.hidden = true; ui.save.hidden = true; ui.prepared.hidden = true;
|
| 136 |
+
showTimings([]); last = null;
|
| 137 |
+
if (ui.save.href?.startsWith("blob:")) URL.revokeObjectURL(ui.save.href);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
async function load() {
|
| 141 |
+
busy = true; ui.go.disabled = true;
|
| 142 |
+
window.__agate.state = "loading";
|
| 143 |
+
agate = new Agate({ base: BASE, override: MODELS_OVERRIDE, version, ep, variant: VARIANT, optLevel: params.get("opt") || "all" });
|
| 144 |
+
try {
|
| 145 |
+
const t0 = performance.now();
|
| 146 |
+
const st = await agate.load(({ phase, file, loaded, total }) => {
|
| 147 |
+
if (phase === "download") setProgress(loaded / total, `Downloading ${file}`, `${mb(loaded)} / ${mb(total)} MB`);
|
| 148 |
+
else setProgress(1, `Preparing ${file} on ${ep === "webgpu" ? "WebGPU" : "CPU"}`, `${mb(total)} MB`);
|
| 149 |
+
}, res());
|
| 150 |
+
const loadMs = performance.now() - t0;
|
| 151 |
+
$("dl-size").textContent = `${st.downloadMB.toFixed(0)} MB`;
|
| 152 |
+
ui.backend.textContent = `backend: ${ep === "webgpu" ? "WebGPU" : "WebAssembly (CPU)"}`;
|
| 153 |
+
const src = st.fromCacheMB > st.downloadMB * 0.99 ? "from browser cache" : "downloaded";
|
| 154 |
+
setProgress(0, "Ready", `Preview ${version} · ${st.downloadMB.toFixed(0)} MB ${src} · ${(loadMs / 1000).toFixed(1)} s`);
|
| 155 |
+
window.__agate = { state: "ready", version, load: { ...st, loadMs, ep } };
|
| 156 |
+
ui.go.textContent = "Generate"; ui.go.disabled = false;
|
| 157 |
+
} catch (e) {
|
| 158 |
+
console.error(e);
|
| 159 |
+
setProgress(0, "Load failed", String(e.message || e));
|
| 160 |
+
window.__agate = { state: "error", version, error: String(e.message || e) };
|
| 161 |
+
ui.go.textContent = "Retry load"; ui.go.disabled = false; agate = null;
|
| 162 |
+
}
|
| 163 |
+
busy = false;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
async function pngWithMetadata() {
|
| 167 |
+
const blob = await new Promise((r) => ui.canvas.toBlob(r, "image/png"));
|
| 168 |
+
const bytes = addPngText(new Uint8Array(await blob.arrayBuffer()), agate.marks.info);
|
| 169 |
+
return new Blob([bytes], { type: "image/png" });
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
function previewFn() {
|
| 173 |
+
thinkerView.reset();
|
| 174 |
+
let planMs = 0, n = 0;
|
| 175 |
+
window.__agate.thinker = { steps: 0, planMs: 0 };
|
| 176 |
+
return ({ step, steps, plan, x1, hw }) => {
|
| 177 |
+
const t0 = performance.now();
|
| 178 |
+
ui.thinker.hidden = false;
|
| 179 |
+
thinkerView.drawPlan(plan);
|
| 180 |
+
thinkerView.drawPrediction(x1, hw);
|
| 181 |
+
ui.thinkerStep.textContent = `${step} / ${steps}`;
|
| 182 |
+
planMs += performance.now() - t0; n++;
|
| 183 |
+
window.__agate.thinker = { steps: n, drawMsMean: planMs / n };
|
| 184 |
+
};
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
async function generate(opts = {}) {
|
| 188 |
+
busy = true; stop = false;
|
| 189 |
+
ui.go.textContent = "Stop"; ui.frame.classList.add("busy");
|
| 190 |
+
window.__agate.state = "generating";
|
| 191 |
+
const steps = opts.steps ?? Number(ui.steps.value);
|
| 192 |
+
try {
|
| 193 |
+
setProgress(0, "Encoding prompt", "");
|
| 194 |
+
const r = await agate.generate({
|
| 195 |
+
prompt: opts.prompt ?? ui.prompt.value.trim(), negative: opts.negative ?? ui.negative.value.trim(),
|
| 196 |
+
seed: Number(ui.seed.value) >>> 0, steps, cfg: opts.cfg ?? Number(ui.cfg.value), noise: opts.noise ?? null,
|
| 197 |
+
watermark: opts.watermark ?? true,
|
| 198 |
+
onStep: (i, n) => setProgress(i / n, `Step ${i} / ${n}`, ""),
|
| 199 |
+
onPreview: (opts.thinker ?? ui.showThinker.checked) ? previewFn() : null,
|
| 200 |
+
shouldStop: () => stop,
|
| 201 |
+
});
|
| 202 |
+
const ctx = ui.canvas.getContext("2d");
|
| 203 |
+
ui.canvas.width = r.width; ui.canvas.height = r.height;
|
| 204 |
+
ctx.putImageData(new ImageData(r.rgba, r.width, r.height), 0, 0);
|
| 205 |
+
ui.placeholder.hidden = true;
|
| 206 |
+
ui.aiNote.hidden = false;
|
| 207 |
+
const T = r.timings;
|
| 208 |
+
setProgress(1, "Done", `${(T.totalMs / 1000).toFixed(1)} s`);
|
| 209 |
+
showTimings([
|
| 210 |
+
["text encode", `${T.textMs.toFixed(0)} ms (bucket ${T.bucket})`],
|
| 211 |
+
["per step", `${T.stepMs.toFixed(0)} ms × ${steps} (first ${T.firstStepMs.toFixed(0)} ms)`],
|
| 212 |
+
["decode + mark", `${T.decodeMs.toFixed(0)} + ${T.markMs.toFixed(0)} ms`],
|
| 213 |
+
["total", `${(T.totalMs / 1000).toFixed(2)} s`],
|
| 214 |
+
]);
|
| 215 |
+
const p = r.prepared;
|
| 216 |
+
const changed = agate.mr && (p.text !== (opts.prompt ?? ui.prompt.value.trim()) || p.negative || p.countsNonzero);
|
| 217 |
+
ui.prepared.hidden = !changed;
|
| 218 |
+
if (changed) ui.prepared.textContent = `Model sees: ${p.text}${p.negative ? ` · avoid: ${p.negative}` : ""}${p.countsNonzero ? ` · count code on ${p.countsNonzero} token(s)` : ""}`;
|
| 219 |
+
if (ui.save.href?.startsWith("blob:")) URL.revokeObjectURL(ui.save.href);
|
| 220 |
+
ui.save.href = URL.createObjectURL(await pngWithMetadata());
|
| 221 |
+
ui.save.download = `agate-${version}-${r.width}px-seed${Number(ui.seed.value) >>> 0}.png`;
|
| 222 |
+
ui.save.hidden = false;
|
| 223 |
+
last = r;
|
| 224 |
+
window.__agate = { ...window.__agate, state: "done", timings: T, prepared: p, size: [r.width, r.height], watermarked: r.watermarked,
|
| 225 |
+
thinkerShown: !ui.thinker.hidden };
|
| 226 |
+
return r;
|
| 227 |
+
} catch (e) {
|
| 228 |
+
if (String(e.message) === "stopped") setProgress(0, "Stopped", "");
|
| 229 |
+
else { console.error(e); setProgress(0, "Error", String(e.message || e)); window.__agate.error = String(e.message || e); }
|
| 230 |
+
window.__agate.state = "done";
|
| 231 |
+
} finally {
|
| 232 |
+
busy = false; ui.go.textContent = "Generate"; ui.frame.classList.remove("busy");
|
| 233 |
+
}
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
ui.go.onclick = async () => {
|
| 237 |
+
if (busy && agate?.sessions) { stop = true; return; }
|
| 238 |
+
if (busy) return;
|
| 239 |
+
if (!agate) return load();
|
| 240 |
+
return generate();
|
| 241 |
+
};
|
| 242 |
+
ui.prompt.addEventListener("keydown", (e) => { if (e.key === "Enter" && (e.ctrlKey || e.metaKey) && agate && !busy) generate(); });
|
| 243 |
+
|
| 244 |
+
// Parity self-test: ?parity=1 runs the chosen version's fixture (export/parity_full*.py): same initial noise as the
|
| 245 |
+
// PyTorch package, watermark off (the fixture holds unmarked pixels); then checks the mark on the marked image.
|
| 246 |
+
async function parity() {
|
| 247 |
+
const dir = version === "001" ? "test/" : version === "002" ? "test/002/" : `test/003_${res()}/`;
|
| 248 |
+
const fx = await (await fetch(`${dir}parity.json`)).json();
|
| 249 |
+
const bin = async (f) => new Uint8Array(await (await fetch(`${dir}${f}`)).arrayBuffer());
|
| 250 |
+
const noise = new Float32Array((await bin("parity_noise.bin")).buffer);
|
| 251 |
+
const refZ = new Float32Array((await bin("parity_latent.bin")).buffer);
|
| 252 |
+
const refImg = await bin("parity_image.bin"); // HWC uint8
|
| 253 |
+
ui.prompt.value = fx.prompt; ui.steps.value = fx.steps; ui.cfg.value = fx.cfg; ui.negative.value = "";
|
| 254 |
+
const r = await generate({ prompt: fx.prompt, negative: "", steps: fx.steps, cfg: fx.cfg, noise, watermark: false });
|
| 255 |
+
let dz = 0, dzs = 0, di = 0, dis = 0, se = 0;
|
| 256 |
+
for (let k = 0; k < refZ.length; k++) { const d = Math.abs(r.latent[k] - refZ[k]); dz = Math.max(dz, d); dzs += d; }
|
| 257 |
+
for (let p = 0; p < refImg.length / 3; p++) for (let c = 0; c < 3; c++) {
|
| 258 |
+
const d = Math.abs(r.rgba[p * 4 + c] - refImg[p * 3 + c]); di = Math.max(di, d); dis += d; se += d * d;
|
| 259 |
+
}
|
| 260 |
+
const psnr = 10 * Math.log10(255 * 255 / Math.max(1e-9, se / refImg.length));
|
| 261 |
+
const prepOk = fx.prepared === undefined || (r.prepared.text === fx.prepared && r.prepared.negative === fx.negative);
|
| 262 |
+
// the same image marked, read back in the page
|
| 263 |
+
const { embedWatermark } = await import("./marking.js");
|
| 264 |
+
const marked = Uint8ClampedArray.from(r.rgba);
|
| 265 |
+
embedWatermark(marked, r.width, r.height, agate.marks.payload);
|
| 266 |
+
const rb = readWatermark(marked, r.width, r.height, agate.marks.payload);
|
| 267 |
+
const res_ = { version, resolution: res(), latentMaxAbs: dz, latentMeanAbs: dzs / refZ.length, imageMaxAbs: di, imageMeanAbs: dis / refImg.length,
|
| 268 |
+
psnr, preparedMatchesPython: prepOk, watermarkReadback: rb.text, watermarkBitAcc: rb.bitAccuracy, ep, timings: r.timings };
|
| 269 |
+
const el = $("parity"); el.hidden = false;
|
| 270 |
+
el.innerHTML = `<span class="label"><i class="sq red"></i>Parity vs PyTorch package · Preview ${version} · ${res()} px (${ep})</span>
|
| 271 |
+
<p>final latent max |Δ| ${dz.toFixed(4)} (mean ${res_.latentMeanAbs.toFixed(5)}) · image max |Δ| ${di}/255 (mean ${res_.imageMeanAbs.toFixed(3)}, PSNR ${psnr.toFixed(1)} dB)
|
| 272 |
+
· prompt pipeline ${prepOk ? "identical" : "DIFFERENT"} · watermark read back: ${rb.text} (${(rb.bitAccuracy * 100).toFixed(0)}% bits)</p>`;
|
| 273 |
+
window.__agate = { ...window.__agate, parity: res_, state: "parity-done" };
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
// Golden test of the prompt pipeline in this browser (tokenizers.js + prompt.js) against the Python package.
|
| 277 |
+
async function golden() {
|
| 278 |
+
const G = await (await fetch("test/prompt_golden.json")).json();
|
| 279 |
+
const tok = agate.tok, fails = [];
|
| 280 |
+
const enc = (s) => agate.tokenize(s).map(Number);
|
| 281 |
+
for (const c of G.cases) {
|
| 282 |
+
const [text, neg] = preparePrompt(c.prompt);
|
| 283 |
+
const ids = enc(text);
|
| 284 |
+
const cv = countVector(tok, text, ids, ids.length), counts = {};
|
| 285 |
+
cv.forEach((v, j) => { if (v) counts[String(j)] = v; });
|
| 286 |
+
const ok = text === c.prepared && neg === c.negative && JSON.stringify(ids) === JSON.stringify(c.ids)
|
| 287 |
+
&& JSON.stringify(enc(neg)) === JSON.stringify(c.neg_ids) && JSON.stringify(counts) === JSON.stringify(c.counts);
|
| 288 |
+
if (!ok) fails.push(c.prompt);
|
| 289 |
+
}
|
| 290 |
+
const res_ = { cases: G.cases.length, identical: G.cases.length - fails.length, fails };
|
| 291 |
+
const el = $("parity"); el.hidden = false;
|
| 292 |
+
el.innerHTML += `<span class="label"><i class="sq red"></i>Prompt pipeline golden test</span><p>${res_.identical} / ${res_.cases} prompts identical to the Python package (prompt, negative, token ids, count code)</p>`;
|
| 293 |
+
window.__agate = { ...window.__agate, golden: res_ };
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
// For the automated test: the PNG exactly as "Save PNG" gives it, base64
|
| 297 |
+
window.__agateSavedPng = async () => {
|
| 298 |
+
const b = new Uint8Array(await (await pngWithMetadata()).arrayBuffer());
|
| 299 |
+
let s = ""; for (let i = 0; i < b.length; i += 0x8000) s += String.fromCharCode(...b.subarray(i, i + 0x8000));
|
| 300 |
+
return btoa(s);
|
| 301 |
+
};
|
| 302 |
+
window.agateClearCache = clearCache;
|
| 303 |
+
|
| 304 |
+
(async () => {
|
| 305 |
+
renderVersion();
|
| 306 |
+
if (!FORCE_EP) {
|
| 307 |
+
const s = await webgpuStatus();
|
| 308 |
+
if (!s.ok) {
|
| 309 |
+
gpuOk = false; ep = "wasm";
|
| 310 |
+
$("nogpu").hidden = false; $("nogpu-why").textContent = s.why;
|
| 311 |
+
ui.go.disabled = true; ui.go.textContent = "WebGPU required";
|
| 312 |
+
window.__agate = { state: "no-webgpu", why: s.why, version };
|
| 313 |
+
$("use-wasm").onclick = () => { $("nogpu").hidden = true; gpuOk = true; ui.go.textContent = "Load model (CPU)"; ui.go.disabled = false; };
|
| 314 |
+
return;
|
| 315 |
+
}
|
| 316 |
+
ui.backend.textContent = `backend: WebGPU${s.adapter ? " · " + s.adapter : ""}`;
|
| 317 |
+
}
|
| 318 |
+
ui.go.disabled = false;
|
| 319 |
+
ui.go.textContent = "Load model";
|
| 320 |
+
if (params.has("autoload") || PARITY || GOLDEN || await hasCachedModel(version)) { // second visit: load straight from cache
|
| 321 |
+
await load();
|
| 322 |
+
if (GOLDEN && agate && version === "003") await golden();
|
| 323 |
+
if (PARITY && agate) await parity();
|
| 324 |
+
if (GOLDEN && !PARITY) window.__agate.state = "golden-done";
|
| 325 |
+
}
|
| 326 |
+
})();
|
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// AI-generated content marking for the images this page makes (EU AI Act Art. 50(2)), the same marks as the
|
| 2 |
+
// Python packages (agate/marking.py of Logolabs/agate-preview-002 / -003):
|
| 3 |
+
// * an invisible watermark in the pixels: invisible-watermark's (MIT) 'dwtDctSvd' method, fixed 64-bit payload
|
| 4 |
+
// "AGATE" + release ("AGATE001" / "AGATE002" / "AGATE003"), ported from the library with its exact conventions
|
| 5 |
+
// (OpenCV 8-bit YUV, U channel only, Haar LL band, 4x4 DCT + SVD, s0 quantised to 36, the library's swapped
|
| 6 |
+
// detail order in the inverse DWT, numpy's truncating uint8 cast). agate.detect_watermark() in Python reads it.
|
| 7 |
+
// * provenance text chunks (tEXt) in the downloaded PNG: ai_generated, generator, model, watermark -- the keys
|
| 8 |
+
// and values the Python packages write. The prompt is NOT written.
|
| 9 |
+
// Neither mark is tamper-proof; see the model cards.
|
| 10 |
+
|
| 11 |
+
export const METHOD = "dwtDctSvd";
|
| 12 |
+
const SCALE = 36, BLOCK = 4;
|
| 13 |
+
const S2 = 0.7071067811865476; // PyWavelets' Haar tap, 1/sqrt(2)
|
| 14 |
+
|
| 15 |
+
export function marks(release) {
|
| 16 |
+
const payload = "AGATE" + release; // 8 ASCII bytes = 64 bits
|
| 17 |
+
return {
|
| 18 |
+
payload,
|
| 19 |
+
info: {
|
| 20 |
+
ai_generated: "true",
|
| 21 |
+
generator: `Agate Preview ${release} (LogoLabs)`,
|
| 22 |
+
model: `Logolabs/agate-preview-${release}`,
|
| 23 |
+
watermark: `invisible-watermark ${METHOD}, payload ${payload}`,
|
| 24 |
+
},
|
| 25 |
+
};
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
const desc = (x) => (x + 8192) >> 14; // OpenCV CV_DESCALE(x, 14)
|
| 29 |
+
const sat = (x) => (x < 0 ? 0 : x > 255 ? 255 : x);
|
| 30 |
+
const u8unsafe = (x) => { const t = Math.trunc(x) % 256; return t < 0 ? t + 256 : t; }; // numpy float -> uint8
|
| 31 |
+
|
| 32 |
+
// orthonormal 4-point DCT-II matrix (cv2.dct on a 4x4 block = C X C^T)
|
| 33 |
+
const C = [];
|
| 34 |
+
for (let k = 0; k < BLOCK; k++) {
|
| 35 |
+
C.push([]);
|
| 36 |
+
for (let n = 0; n < BLOCK; n++) C[k].push(Math.sqrt(k === 0 ? 1 / BLOCK : 2 / BLOCK) * Math.cos(Math.PI * (2 * n + 1) * k / (2 * BLOCK)));
|
| 37 |
+
}
|
| 38 |
+
function mul(A, B) { const R = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]; for (let i = 0; i < 4; i++) for (let j = 0; j < 4; j++) { let s = 0; for (let k = 0; k < 4; k++) s += A[i][k] * B[k][j]; R[i][j] = s; } return R; }
|
| 39 |
+
const T = (A) => A[0].map((_, j) => A.map((r) => r[j]));
|
| 40 |
+
const CT = T(C);
|
| 41 |
+
|
| 42 |
+
// largest singular value of a 4x4 matrix and its singular vectors (Jacobi on D^T D)
|
| 43 |
+
function topSVD(D) {
|
| 44 |
+
const M = mul(T(D), D);
|
| 45 |
+
const V = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
|
| 46 |
+
for (let sweep = 0; sweep < 30; sweep++) {
|
| 47 |
+
let off = 0;
|
| 48 |
+
for (let p = 0; p < 4; p++) for (let q = p + 1; q < 4; q++) off += M[p][q] * M[p][q];
|
| 49 |
+
if (off < 1e-30) break;
|
| 50 |
+
for (let p = 0; p < 4; p++) for (let q = p + 1; q < 4; q++) {
|
| 51 |
+
if (Math.abs(M[p][q]) < 1e-300) continue;
|
| 52 |
+
const th = (M[q][q] - M[p][p]) / (2 * M[p][q]);
|
| 53 |
+
const t = Math.sign(th || 1) / (Math.abs(th) + Math.sqrt(th * th + 1));
|
| 54 |
+
const c = 1 / Math.sqrt(t * t + 1), s = t * c;
|
| 55 |
+
for (let k = 0; k < 4; k++) { const a = M[k][p], b = M[k][q]; M[k][p] = c * a - s * b; M[k][q] = s * a + c * b; }
|
| 56 |
+
for (let k = 0; k < 4; k++) { const a = M[p][k], b = M[q][k]; M[p][k] = c * a - s * b; M[q][k] = s * a + c * b; }
|
| 57 |
+
for (let k = 0; k < 4; k++) { const a = V[k][p], b = V[k][q]; V[k][p] = c * a - s * b; V[k][q] = s * a + c * b; }
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
let best = 0;
|
| 61 |
+
for (let i = 1; i < 4; i++) if (M[i][i] > M[best][best]) best = i;
|
| 62 |
+
const s0 = Math.sqrt(Math.max(0, M[best][best]));
|
| 63 |
+
let v = V.map((r) => r[best]), u;
|
| 64 |
+
if (s0 < 1e-12) { u = [1, 0, 0, 0]; v = [1, 0, 0, 0]; }
|
| 65 |
+
else u = D.map((r) => (r[0] * v[0] + r[1] * v[1] + r[2] * v[2] + r[3] * v[3]) / s0);
|
| 66 |
+
return { s0, u, v };
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
function payloadBits(payload) {
|
| 70 |
+
const bytes = new TextEncoder().encode(payload), bits = [];
|
| 71 |
+
for (const b of bytes) for (let k = 7; k >= 0; k--) bits.push((b >> k) & 1);
|
| 72 |
+
return bits;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
// U channel (OpenCV BGR2YUV, 8 bit) of an RGBA buffer -> Int32Array(W*H), plus Y and V for the inverse
|
| 76 |
+
function toYUV(rgba, W, H) {
|
| 77 |
+
const Y = new Int32Array(W * H), U = new Int32Array(W * H), V = new Int32Array(W * H);
|
| 78 |
+
for (let p = 0; p < W * H; p++) {
|
| 79 |
+
const r = rgba[4 * p], g = rgba[4 * p + 1], b = rgba[4 * p + 2];
|
| 80 |
+
const y = desc(b * 1868 + g * 9617 + r * 4899);
|
| 81 |
+
Y[p] = y; U[p] = sat(desc((b - y) * 8061 + (128 << 14))); V[p] = sat(desc((r - y) * 14369 + (128 << 14)));
|
| 82 |
+
}
|
| 83 |
+
return { Y, U, V };
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
// LL band of the Haar DWT of U over the (H//4*4, W//4*4) region; -> {ca, ch, cv, cd} as Float64Array (h2 x w2)
|
| 87 |
+
function haar(U, W, R, Cc) {
|
| 88 |
+
const h2 = R / 2, w2 = Cc / 2, n = h2 * w2;
|
| 89 |
+
const ca = new Float64Array(n), ch = new Float64Array(n), cv = new Float64Array(n), cd = new Float64Array(n);
|
| 90 |
+
for (let i = 0; i < h2; i++) for (let j = 0; j < w2; j++) {
|
| 91 |
+
const a = U[(2 * i) * W + 2 * j], b = U[(2 * i) * W + 2 * j + 1], c = U[(2 * i + 1) * W + 2 * j], d = U[(2 * i + 1) * W + 2 * j + 1];
|
| 92 |
+
const k = i * w2 + j;
|
| 93 |
+
// PyWavelets' exact float order: pairs along rows first (a|c, b|d), then along columns
|
| 94 |
+
const l0 = a * S2 + c * S2, l1 = b * S2 + d * S2, h0 = a * S2 - c * S2, h1 = b * S2 - d * S2;
|
| 95 |
+
ca[k] = l0 * S2 + l1 * S2; cv[k] = l0 * S2 - l1 * S2; ch[k] = h0 * S2 + h1 * S2; cd[k] = h0 * S2 - h1 * S2;
|
| 96 |
+
}
|
| 97 |
+
return { ca, ch, cv, cd, h2, w2 };
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
function blockAt(ca, w2, bi, bj) { const B = []; for (let r = 0; r < 4; r++) { B.push([]); for (let c = 0; c < 4; c++) B[r].push(ca[(bi * 4 + r) * w2 + bj * 4 + c]); } return B; }
|
| 101 |
+
|
| 102 |
+
// Embed `payload` in place into rgba (Uint8ClampedArray / Uint8Array, W*H*4). Needs W*H >= 256*256.
|
| 103 |
+
export function embedWatermark(rgba, W, H, payload) {
|
| 104 |
+
if (W * H < 256 * 256) throw new Error("watermark: image too small (needs at least 256 x 256)");
|
| 105 |
+
const bits = payloadBits(payload);
|
| 106 |
+
const { Y, U, V } = toYUV(rgba, W, H);
|
| 107 |
+
const R = Math.floor(H / 4) * 4, Cc = Math.floor(W / 4) * 4;
|
| 108 |
+
const { ca, ch, cv, cd, h2, w2 } = haar(U, W, R, Cc);
|
| 109 |
+
const nbi = Math.floor(h2 / 4), nbj = Math.floor(w2 / 4);
|
| 110 |
+
let num = 0;
|
| 111 |
+
for (let bi = 0; bi < nbi; bi++) for (let bj = 0; bj < nbj; bj++, num++) {
|
| 112 |
+
const D = mul(mul(C, blockAt(ca, w2, bi, bj)), CT);
|
| 113 |
+
const { s0, u, v } = topSVD(D);
|
| 114 |
+
const s1 = (Math.floor(s0 / SCALE) + 0.25 + 0.5 * bits[num % bits.length]) * SCALE;
|
| 115 |
+
const Dn = D.map((row, i) => row.map((x, j) => x + (s1 - s0) * u[i] * v[j]));
|
| 116 |
+
const Bn = mul(mul(CT, Dn), C);
|
| 117 |
+
for (let r = 0; r < 4; r++) for (let c = 0; c < 4; c++) ca[(bi * 4 + r) * w2 + bj * 4 + c] = Bn[r][c];
|
| 118 |
+
}
|
| 119 |
+
// inverse Haar with the library's swapped details (cv as H, ch as V), truncating uint8 cast
|
| 120 |
+
for (let i = 0; i < h2; i++) for (let j = 0; j < w2; j++) {
|
| 121 |
+
// pywt.idwt2((ca, (cv, ch, cd))): the library's swapped details; PyWavelets' float order (columns, then rows)
|
| 122 |
+
const k = i * w2 + j, A = ca[k], hIn = cv[k], vIn = ch[k], d = cd[k];
|
| 123 |
+
const L0 = A * S2 + vIn * S2, L1 = A * S2 - vIn * S2, H0 = hIn * S2 + d * S2, H1 = hIn * S2 - d * S2;
|
| 124 |
+
U[(2 * i) * W + 2 * j] = u8unsafe(L0 * S2 + H0 * S2);
|
| 125 |
+
U[(2 * i) * W + 2 * j + 1] = u8unsafe(L1 * S2 + H1 * S2);
|
| 126 |
+
U[(2 * i + 1) * W + 2 * j] = u8unsafe(L0 * S2 - H0 * S2);
|
| 127 |
+
U[(2 * i + 1) * W + 2 * j + 1] = u8unsafe(L1 * S2 - H1 * S2);
|
| 128 |
+
}
|
| 129 |
+
for (let p = 0; p < W * H; p++) { // OpenCV YUV2BGR, 8 bit
|
| 130 |
+
const y = Y[p], uu = U[p] - 128, vq = V[p] - 128;
|
| 131 |
+
rgba[4 * p + 2] = sat(y + desc(uu * 33292));
|
| 132 |
+
rgba[4 * p + 1] = sat(y + desc(uu * -6472 + vq * -9519));
|
| 133 |
+
rgba[4 * p] = sat(y + desc(vq * 18678));
|
| 134 |
+
}
|
| 135 |
+
return rgba;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
// -> {bits, text, bitAccuracy} against `payload` (a self-check; the reference detector is the Python one)
|
| 139 |
+
export function readWatermark(rgba, W, H, payload) {
|
| 140 |
+
const want = payloadBits(payload), n = want.length;
|
| 141 |
+
const { U } = toYUV(rgba, W, H);
|
| 142 |
+
const R = Math.floor(H / 4) * 4, Cc = Math.floor(W / 4) * 4;
|
| 143 |
+
const { ca, h2, w2 } = haar(U, W, R, Cc);
|
| 144 |
+
const nbi = Math.floor(h2 / 4), nbj = Math.floor(w2 / 4);
|
| 145 |
+
const sum = new Float64Array(n), cnt = new Float64Array(n);
|
| 146 |
+
let num = 0;
|
| 147 |
+
for (let bi = 0; bi < nbi; bi++) for (let bj = 0; bj < nbj; bj++, num++) {
|
| 148 |
+
const { s0 } = topSVD(mul(mul(C, blockAt(ca, w2, bi, bj)), CT));
|
| 149 |
+
sum[num % n] += (s0 % SCALE) > SCALE * 0.5 ? 1 : 0; cnt[num % n] += 1;
|
| 150 |
+
}
|
| 151 |
+
const bits = Array.from(sum, (s, k) => (s / cnt[k]) * 255 > 127 ? 1 : 0);
|
| 152 |
+
let same = 0; bits.forEach((b, k) => { if (b === want[k]) same++; });
|
| 153 |
+
const bytes = []; for (let k = 0; k < n; k += 8) { let b = 0; for (let q = 0; q < 8; q++) b = (b << 1) | bits[k + q]; bytes.push(b); }
|
| 154 |
+
return { bits, text: String.fromCharCode(...bytes), bitAccuracy: same / n };
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
// ---- PNG tEXt chunks ---------------------------------------------------------------------------
|
| 158 |
+
let CRC_TABLE = null;
|
| 159 |
+
function crc32(bytes) {
|
| 160 |
+
if (!CRC_TABLE) { CRC_TABLE = new Uint32Array(256); for (let n = 0; n < 256; n++) { let c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; CRC_TABLE[n] = c >>> 0; } }
|
| 161 |
+
let c = 0xffffffff;
|
| 162 |
+
for (const b of bytes) c = CRC_TABLE[(c ^ b) & 0xff] ^ (c >>> 8);
|
| 163 |
+
return (c ^ 0xffffffff) >>> 0;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
// PNG bytes -> PNG bytes with one tEXt chunk per entry of `info` (Latin-1 keys/values), inserted before IEND.
|
| 167 |
+
export function addPngText(png, info) {
|
| 168 |
+
const latin1 = (s) => Uint8Array.from(s, (ch) => { const c = ch.charCodeAt(0); return c < 256 ? c : 63; });
|
| 169 |
+
const chunks = [];
|
| 170 |
+
for (const [k, v] of Object.entries(info)) {
|
| 171 |
+
const data = new Uint8Array([...latin1(k), 0, ...latin1(String(v))]);
|
| 172 |
+
const typeData = new Uint8Array([116, 69, 88, 116, ...data]); // "tEXt"
|
| 173 |
+
const out = new Uint8Array(12 + data.length), dv = new DataView(out.buffer);
|
| 174 |
+
dv.setUint32(0, data.length); out.set(typeData, 4); dv.setUint32(8 + data.length, crc32(typeData));
|
| 175 |
+
chunks.push(out);
|
| 176 |
+
}
|
| 177 |
+
// find IEND (the last chunk): 12 bytes from the end in every well-formed PNG
|
| 178 |
+
const iend = png.length - 12;
|
| 179 |
+
const extra = chunks.reduce((s, c) => s + c.length, 0);
|
| 180 |
+
const res = new Uint8Array(png.length + extra);
|
| 181 |
+
res.set(png.subarray(0, iend), 0);
|
| 182 |
+
let o = iend;
|
| 183 |
+
for (const c of chunks) { res.set(c, o); o += c.length; }
|
| 184 |
+
res.set(png.subarray(iend), o);
|
| 185 |
+
return res;
|
| 186 |
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Agate Preview 003 prompt pipeline, a line-by-line port of agate/prompt_norm.py (+ AgatePipeline.prepare
|
| 2 |
+
// and prompt_norm.count_tensor) of Logolabs/agate-preview-003. Checked against the Python package by
|
| 3 |
+
// test/prompt_golden.json (tools: export/prompt_golden.py writes it, test/prompt_golden.mjs or ?golden=1 checks it).
|
| 4 |
+
//
|
| 5 |
+
// Python-compatibility notes (the reason for the helpers below):
|
| 6 |
+
// * Python's \s, \d, \b and str.isalnum/isspace are Unicode-aware; JS's are ASCII. PY_WS, \p{Nd} and the
|
| 7 |
+
// Unicode word-boundary WB mirror Python's definitions (\w = letters, numbers, underscore).
|
| 8 |
+
// * Python counts code points; JS strings count UTF-16 units. Lengths that gate behaviour use cpLen().
|
| 9 |
+
// * Token offsets come from the HF fast tokenizer in Python; here they are rebuilt from the byte-level BPE
|
| 10 |
+
// tokens (tokenOffsets), in UTF-16 units, which gives the same overlaps as Python's code-point offsets.
|
| 11 |
+
|
| 12 |
+
const PY_WS = "\\t\\n\\x0b\\x0c\\r\\x1c-\\x20\\x85\\xa0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
|
| 13 |
+
const S = `[${PY_WS}]`; // Python's \s
|
| 14 |
+
const NS = `[^${PY_WS}]`; // Python's \S
|
| 15 |
+
const W = "[\\p{L}\\p{N}_]"; // Python's \w (str patterns)
|
| 16 |
+
const WB = `(?:(?<=${W})(?!${W})|(?<!${W})(?=${W}))`; // Python's \b
|
| 17 |
+
const D = "\\p{Nd}"; // Python's \d
|
| 18 |
+
const re = (src, flags = "") => new RegExp(src, flags.includes("u") ? flags : flags + "u");
|
| 19 |
+
|
| 20 |
+
const NUM_LIST = "zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty".split(" ");
|
| 21 |
+
const NUM_WORDS = new Map(NUM_LIST.map((w, i) => [w, i]));
|
| 22 |
+
NUM_WORDS.set("dozen", 12);
|
| 23 |
+
const WORD_OF = new Map(NUM_LIST.map((w, i) => [i, w]));
|
| 24 |
+
const NOT_COUNT_NEXT = new Set(["pm", "am", "o'clock", "percent", "%", "years", "year", "hours", "hour", "minutes", "minute",
|
| 25 |
+
"seconds", "second", "times", "px", "cm", "mm", "m", "km", "kg", "g", "inch", "inches", "feet",
|
| 26 |
+
"degrees", "x", "d", "k", "th", "st", "nd", "rd", "of", "o"]);
|
| 27 |
+
const ONE_PRONOUN_PREV = new Set(["the", "this", "that", "no", "each", "every", "any", "some", "which", "another", "someone",
|
| 28 |
+
"everyone", "anyone", "only"]);
|
| 29 |
+
export const SPELL_SEP = " || spell: ";
|
| 30 |
+
const SPELL_MAX_FRAC = 0.6, SPELL_MAX_CHARS = 40, SPELL_MAX_WORDS = 6, SPELL_MAX_SPANS = 3;
|
| 31 |
+
|
| 32 |
+
const QUOTE_SRC = '"[^"]*"|“[^”]*”';
|
| 33 |
+
const cpLen = (s) => { let n = 0; for (const _ of s) n++; return n; }; // len() in Python
|
| 34 |
+
const isSpace = (ch) => re(`^${S}$`).test(ch);
|
| 35 |
+
const pyStrip = (s, chars = null) => {
|
| 36 |
+
const f = chars === null ? isSpace : (c) => chars.includes(c);
|
| 37 |
+
let a = 0, b = s.length;
|
| 38 |
+
while (a < b && f(s[a])) a++;
|
| 39 |
+
while (b > a && f(s[b - 1])) b--;
|
| 40 |
+
return s.slice(a, b);
|
| 41 |
+
};
|
| 42 |
+
const pySplit = (s) => s.split(re(`${S}+`)).filter((x) => x.length > 0); // str.split()
|
| 43 |
+
const isAlnum = (ch) => /^[\p{L}\p{N}]$/u.test(ch);
|
| 44 |
+
const isAlpha = (w) => w.length > 0 && /^\p{L}+$/u.test(w);
|
| 45 |
+
const isUpper = (w) => w !== w.toLowerCase() && w === w.toUpperCase(); // ASCII words only reach these
|
| 46 |
+
const isLower = (w) => w !== w.toUpperCase() && w === w.toLowerCase();
|
| 47 |
+
const isDigits = (w) => /^\p{Nd}+$/u.test(w);
|
| 48 |
+
|
| 49 |
+
function digitValue(cp) { // value of one \p{Nd} code point: Nd code points come in runs of ten
|
| 50 |
+
let start = cp;
|
| 51 |
+
while (/\p{Nd}/u.test(String.fromCodePoint(start - 1))) start--;
|
| 52 |
+
return (cp - start) % 10;
|
| 53 |
+
}
|
| 54 |
+
function pyInt(w) { // int() of a \p{Nd}+ string
|
| 55 |
+
let n = 0;
|
| 56 |
+
for (const ch of w) n = n * 10 + digitValue(ch.codePointAt(0));
|
| 57 |
+
return n;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
function outsideQuotes(text) {
|
| 61 |
+
const spans = [];
|
| 62 |
+
let last = 0;
|
| 63 |
+
for (const m of text.matchAll(re(QUOTE_SRC, "g"))) { spans.push([last, m.index]); last = m.index + m[0].length; }
|
| 64 |
+
spans.push([last, text.length]);
|
| 65 |
+
return spans.filter(([a, b]) => b > a);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
function mapOutside(text, fn) {
|
| 69 |
+
let out = "", last = 0;
|
| 70 |
+
for (const [a, b] of outsideQuotes(text)) { out += text.slice(last, a) + fn(text.slice(a, b)); last = b; }
|
| 71 |
+
return out + text.slice(last);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
function fixCase(words) {
|
| 75 |
+
const long2 = words.filter((w) => cpLen(w) >= 2 && isAlpha(w));
|
| 76 |
+
if (long2.length && long2.filter(isUpper).length / long2.length >= 0.6) return "shout";
|
| 77 |
+
const long3 = words.filter((w) => cpLen(w) >= 3 && isAlpha(w));
|
| 78 |
+
if (long3.length >= 3) {
|
| 79 |
+
const t = long3.filter((w) => { const c = [...w]; return isUpper(c[0]) && isLower(c.slice(1).join("")); }).length;
|
| 80 |
+
if (t / long3.length >= 0.7) return "title";
|
| 81 |
+
}
|
| 82 |
+
return null;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
export function isJsonCaption(text) {
|
| 86 |
+
const s = pyStrip(text.split(SPELL_SEP)[0]);
|
| 87 |
+
if (!(s.startsWith("{") && s.endsWith("}"))) return false;
|
| 88 |
+
try { const v = JSON.parse(s); return v !== null && typeof v === "object" && !Array.isArray(v); } catch { return false; }
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
function isCountContext(seg, end) {
|
| 92 |
+
const nxt = re(`^${S}*([A-Za-z%']+)`).exec(seg.slice(end));
|
| 93 |
+
return !!nxt && !NOT_COUNT_NEXT.has(nxt[1].toLowerCase());
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
function canonNumbers(seg) {
|
| 97 |
+
return seg.replace(re(`${WB}[A-Za-z]+${WB}|${WB}${D}+${WB}`, "g"), (w, off) => {
|
| 98 |
+
const lw = w.toLowerCase();
|
| 99 |
+
if (NUM_WORDS.has(lw)) return lw;
|
| 100 |
+
if (isDigits(w)) { const n = pyInt(w); if (n >= 1 && n <= 20 && isCountContext(seg, off + w.length)) return WORD_OF.get(n); }
|
| 101 |
+
return w;
|
| 102 |
+
});
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
export function normalize(text) {
|
| 106 |
+
if (isJsonCaption(text)) return text;
|
| 107 |
+
text = pyStrip(text.replace(re(`${S}+`, "g"), " "));
|
| 108 |
+
const words = [];
|
| 109 |
+
for (const [a, b] of outsideQuotes(text)) for (const m of text.slice(a, b).matchAll(re(`[A-Za-z][A-Za-z'\\-]*|${D}+`, "g"))) words.push(m[0]);
|
| 110 |
+
const mode = fixCase(words);
|
| 111 |
+
if (mode === "shout") text = mapOutside(text, (s) => s.toLowerCase());
|
| 112 |
+
else if (mode === "title") {
|
| 113 |
+
const first = re(`^${S}*${NS}+`).exec(text);
|
| 114 |
+
const head = first ? first[0].length : 0;
|
| 115 |
+
text = text.slice(0, head) + mapOutside(text.slice(head), (s) => s.replace(re(`${WB}[A-Z][a-z]*${WB}`, "g"), (m) => m.toLowerCase()));
|
| 116 |
+
}
|
| 117 |
+
return mapOutside(text, canonNumbers);
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
export function findCounts(text) {
|
| 121 |
+
const out = [];
|
| 122 |
+
if (isJsonCaption(text)) return out;
|
| 123 |
+
text = text.split(SPELL_SEP)[0];
|
| 124 |
+
for (const [a, b] of outsideQuotes(text)) {
|
| 125 |
+
const seg = text.slice(a, b);
|
| 126 |
+
const pat = re(`${WB}(?:a${S}+)?(dozen|pair(?=${S}+of${WB}))${WB}|${WB}([A-Za-z]+|${D}+)${WB}`, "gd");
|
| 127 |
+
for (const m of seg.matchAll(pat)) {
|
| 128 |
+
if (m[1] !== undefined) {
|
| 129 |
+
const [s1, e1] = m.indices[1];
|
| 130 |
+
out.push([a + s1, a + e1, m[1].toLowerCase() === "dozen" ? 12 : 2]);
|
| 131 |
+
continue;
|
| 132 |
+
}
|
| 133 |
+
const w = m[2], lw = w.toLowerCase();
|
| 134 |
+
const [s2, e2] = m.indices[2];
|
| 135 |
+
let n;
|
| 136 |
+
if (isDigits(lw)) { n = pyInt(lw); if (!(n >= 1 && n <= 99)) continue; }
|
| 137 |
+
else if (NUM_WORDS.has(lw) && lw !== "zero" && lw !== "dozen") n = NUM_WORDS.get(lw);
|
| 138 |
+
else continue;
|
| 139 |
+
if (!isCountContext(seg, m.index + m[0].length)) continue;
|
| 140 |
+
const prev = seg.slice(0, m.index).match(/[A-Za-z]+/g) || [];
|
| 141 |
+
if (lw === "one" && prev.length && ONE_PRONOUN_PREV.has(prev[prev.length - 1].toLowerCase())) continue;
|
| 142 |
+
out.push([a + s2, a + e2, n]);
|
| 143 |
+
}
|
| 144 |
+
}
|
| 145 |
+
return out;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
export function textSpans(text) {
|
| 149 |
+
const body = text.split(SPELL_SEP)[0];
|
| 150 |
+
const out = [];
|
| 151 |
+
for (const m of body.matchAll(re(QUOTE_SRC, "g"))) {
|
| 152 |
+
const c = pyStrip(m[0].slice(1, -1));
|
| 153 |
+
if (c && [...c].some(isAlnum) && cpLen(c) <= SPELL_MAX_FRAC * cpLen(body) && cpLen(c) <= SPELL_MAX_CHARS && pySplit(c).length <= SPELL_MAX_WORDS) out.push(c);
|
| 154 |
+
}
|
| 155 |
+
return out.slice(0, SPELL_MAX_SPANS);
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
export function spell(content) {
|
| 159 |
+
const words = pySplit(content).map((w) => [...w].filter((ch) => isAlnum(ch) || "&!?'-".includes(ch)));
|
| 160 |
+
return words.filter((w) => w.length).map((w) => w.join(" ")).join(" / ");
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
export function addSpelling(text) {
|
| 164 |
+
if (text.includes(SPELL_SEP) || isJsonCaption(text)) return text;
|
| 165 |
+
const spans = textSpans(text);
|
| 166 |
+
if (!spans.length) return text;
|
| 167 |
+
return text + SPELL_SEP + spans.map(spell).join(" ; ");
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
export function splitNegatives(text) {
|
| 171 |
+
const negs = [];
|
| 172 |
+
if (isJsonCaption(text)) return [text, negs];
|
| 173 |
+
const pat = re(`${S}*,?${S}*${WB}(?:with${S}+)?(?:without|no)${S}+(?:any${S}+)?(?:a${S}+|an${S}+|the${S}+)?([^,.;"“]+?)(?=${S}+and${S}+|[,.;]|$)`, "gi");
|
| 174 |
+
const cut = (seg) => seg.replace(pat, (...g) => { negs.push(pyStrip(g[1])); return ""; });
|
| 175 |
+
let pos = mapOutside(text, cut);
|
| 176 |
+
pos = pyStrip(pos.replace(re(`${S}+([,.;])`, "g"), "$1").replace(re(`${S}+`, "g"), " "), " ,;");
|
| 177 |
+
return [pos, negs];
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
// AgatePipeline.prepare: -> [prompt as the text encoder sees it, negative prompt]
|
| 181 |
+
export function prepare(prompt, negativePrompt = "", normalizeOn = true, spellOn = true) {
|
| 182 |
+
let negs = [];
|
| 183 |
+
if (normalizeOn) [prompt, negs] = splitNegatives(normalize(prompt));
|
| 184 |
+
if (spellOn) prompt = addSpelling(prompt);
|
| 185 |
+
const negative = [pyStrip(negativePrompt), ...negs].filter((n) => n).join(", ");
|
| 186 |
+
return [prompt, negative];
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
// ---- token offsets for a byte-level BPE tokenizer (tokenizers.js has no offset mapping) -----------
|
| 190 |
+
let BYTE_OF = null; // GPT-2 bytes_to_unicode, inverted: char -> byte
|
| 191 |
+
function byteDecoder() {
|
| 192 |
+
if (BYTE_OF) return BYTE_OF;
|
| 193 |
+
const bs = [];
|
| 194 |
+
for (let b = 33; b <= 126; b++) bs.push(b);
|
| 195 |
+
for (let b = 161; b <= 172; b++) bs.push(b);
|
| 196 |
+
for (let b = 174; b <= 255; b++) bs.push(b);
|
| 197 |
+
const cs = bs.slice();
|
| 198 |
+
let n = 0;
|
| 199 |
+
for (let b = 0; b < 256; b++) if (!bs.includes(b)) { bs.push(b); cs.push(256 + n); n++; }
|
| 200 |
+
BYTE_OF = new Map(bs.map((b, i) => [String.fromCodePoint(cs[i]), b]));
|
| 201 |
+
return BYTE_OF;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
// ids: the token ids of `text` ([CLS] ... [SEP]). -> [[start, end], ...] in UTF-16 units of text, [0, 0] for
|
| 205 |
+
// special tokens: Python's offset_mapping (code points) mapped to JS string indices.
|
| 206 |
+
export function tokenOffsets(tok, text, ids) {
|
| 207 |
+
const dec = byteDecoder();
|
| 208 |
+
const nfc = text.normalize("NFC");
|
| 209 |
+
const enc = new TextEncoder();
|
| 210 |
+
// byte index -> [UTF-16 start, UTF-16 end] of the character that byte belongs to
|
| 211 |
+
const charOfByte = [];
|
| 212 |
+
let u = 0;
|
| 213 |
+
for (const ch of nfc) { const nb = enc.encode(ch).length; for (let k = 0; k < nb; k++) charOfByte.push([u, u + ch.length]); u += ch.length; }
|
| 214 |
+
const added = tok.get_added_tokens_decoder?.() ?? new Map();
|
| 215 |
+
const out = [];
|
| 216 |
+
let pos = 0;
|
| 217 |
+
for (const id of ids) {
|
| 218 |
+
const at = added.get(Number(id));
|
| 219 |
+
let nbytes;
|
| 220 |
+
if (at && at.special) { out.push([0, 0]); continue; }
|
| 221 |
+
if (at) nbytes = enc.encode(at.content).length;
|
| 222 |
+
else {
|
| 223 |
+
const s = tok.id_to_token(Number(id));
|
| 224 |
+
nbytes = 0;
|
| 225 |
+
for (const ch of s) nbytes += dec.has(ch) ? 1 : enc.encode(ch).length;
|
| 226 |
+
}
|
| 227 |
+
if (nbytes === 0 || pos >= charOfByte.length) { out.push([0, 0]); continue; }
|
| 228 |
+
const a = charOfByte[pos][0], b = charOfByte[Math.min(pos + nbytes, charOfByte.length) - 1][1];
|
| 229 |
+
out.push([a, b]);
|
| 230 |
+
pos += nbytes;
|
| 231 |
+
}
|
| 232 |
+
// text that NFC changed: offsets refer to the NFC string; the count spans are found in the NFC string too
|
| 233 |
+
return out;
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
// prompt_norm.count_tensor for one prompt: Float32Array(length), the count value at the tokens that spell one.
|
| 237 |
+
export function countVector(tok, text, ids, length) {
|
| 238 |
+
const out = new Float32Array(length);
|
| 239 |
+
const nfc = text.normalize("NFC");
|
| 240 |
+
const spans = findCounts(nfc);
|
| 241 |
+
if (!spans.length) return out;
|
| 242 |
+
const offs = tokenOffsets(tok, nfc, ids);
|
| 243 |
+
for (let j = 0; j < Math.min(length, offs.length); j++) {
|
| 244 |
+
const [a, b] = offs[j];
|
| 245 |
+
if (b <= a) continue;
|
| 246 |
+
for (const [s, e, n] of spans) if (a < e && b > s) out[j] = n;
|
| 247 |
+
}
|
| 248 |
+
return out;
|
| 249 |
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// "Show thinker": the live previews of the ComfyUI nodes (agate-comfyui nodes.py, _progress, live_preview
|
| 2 |
+
// "side_by_side"), computed the same way so the two frontends show the same thing:
|
| 3 |
+
// * the plan: the thinker's output for the conditional branch, 640 channels on a 16 x 16 grid. Its cells are
|
| 4 |
+
// projected on the top 3 principal components (fitted once, at the first step, centred over the cells; each
|
| 5 |
+
// component's sign fixed so its largest-magnitude loading is positive) and mapped to RGB with that step's 2% /
|
| 6 |
+
// 98% quantiles -- a fixed basis, so a colour keeps its meaning while the plan evolves;
|
| 7 |
+
// * the image the model expects at the end, x1 = z + (1 - t) v (guided velocity), shown with ComfyUI's SD 1.5
|
| 8 |
+
// latent -> RGB factors (its "latent2rgb" preview).
|
| 9 |
+
|
| 10 |
+
const RGB_FACTORS = [[0.3512, 0.2297, 0.3227], [0.3250, 0.4974, 0.2350], [-0.2829, 0.1762, 0.2721], [-0.2120, -0.2616, -0.7177]];
|
| 11 |
+
|
| 12 |
+
// top-k eigenvectors of the (d x d) Gram matrix X^T X of centred X (n x d), as torch.linalg.eigh gives them (sign
|
| 13 |
+
// aside), via the smaller (n x n) X X^T: block (subspace) iteration with a 12-vector block + Rayleigh-Ritz, which
|
| 14 |
+
// separates near-equal eigenvalues that plain power iteration mixes; then v = X^T u / |X^T u|.
|
| 15 |
+
function jacobiEig(A, m) { // symmetric m x m (Float64Array) -> {vals, vecs (column-major rows)}
|
| 16 |
+
const a = Float64Array.from(A), V = new Float64Array(m * m);
|
| 17 |
+
for (let i = 0; i < m; i++) V[i * m + i] = 1;
|
| 18 |
+
for (let sweep = 0; sweep < 60; sweep++) {
|
| 19 |
+
let off = 0;
|
| 20 |
+
for (let p = 0; p < m; p++) for (let q = p + 1; q < m; q++) off += a[p * m + q] ** 2;
|
| 21 |
+
if (off < 1e-24) break;
|
| 22 |
+
for (let p = 0; p < m; p++) for (let q = p + 1; q < m; q++) {
|
| 23 |
+
const apq = a[p * m + q];
|
| 24 |
+
if (Math.abs(apq) < 1e-300) continue;
|
| 25 |
+
const th = (a[q * m + q] - a[p * m + p]) / (2 * apq);
|
| 26 |
+
const t = Math.sign(th || 1) / (Math.abs(th) + Math.sqrt(th * th + 1)), c = 1 / Math.sqrt(t * t + 1), s = t * c;
|
| 27 |
+
for (let k = 0; k < m; k++) { const x = a[k * m + p], y = a[k * m + q]; a[k * m + p] = c * x - s * y; a[k * m + q] = s * x + c * y; }
|
| 28 |
+
for (let k = 0; k < m; k++) { const x = a[p * m + k], y = a[q * m + k]; a[p * m + k] = c * x - s * y; a[q * m + k] = s * x + c * y; }
|
| 29 |
+
for (let k = 0; k < m; k++) { const x = V[k * m + p], y = V[k * m + q]; V[k * m + p] = c * x - s * y; V[k * m + q] = s * x + c * y; }
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
return { vals: Array.from({ length: m }, (_, i) => a[i * m + i]), V };
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function topComponents(X, n, d, k = 3, iters = 60, block = 12) {
|
| 36 |
+
const G = new Float64Array(n * n);
|
| 37 |
+
for (let i = 0; i < n; i++) for (let j = i; j < n; j++) {
|
| 38 |
+
let s = 0; const ri = i * d, rj = j * d;
|
| 39 |
+
for (let a = 0; a < d; a++) s += X[ri + a] * X[rj + a];
|
| 40 |
+
G[i * n + j] = G[j * n + i] = s;
|
| 41 |
+
}
|
| 42 |
+
const m = Math.min(block, n);
|
| 43 |
+
let Q = Array.from({ length: m }, (_, c) => Float64Array.from({ length: n }, (_, i) => Math.sin(1.7 * i + 0.9 * c + 1) + 0.01 * c));
|
| 44 |
+
const orth = (B) => { // modified Gram-Schmidt, in place
|
| 45 |
+
for (let c = 0; c < B.length; c++) {
|
| 46 |
+
for (let e = 0; e < c; e++) { let p = 0; for (let i = 0; i < n; i++) p += B[c][i] * B[e][i]; for (let i = 0; i < n; i++) B[c][i] -= p * B[e][i]; }
|
| 47 |
+
let nrm = 0; for (let i = 0; i < n; i++) nrm += B[c][i] ** 2; nrm = Math.sqrt(nrm) || 1;
|
| 48 |
+
for (let i = 0; i < n; i++) B[c][i] /= nrm;
|
| 49 |
+
}
|
| 50 |
+
return B;
|
| 51 |
+
};
|
| 52 |
+
const mul = (q) => { const w = new Float64Array(n); for (let i = 0; i < n; i++) { let s = 0; const gi = i * n; for (let j = 0; j < n; j++) s += G[gi + j] * q[j]; w[i] = s; } return w; };
|
| 53 |
+
orth(Q);
|
| 54 |
+
for (let it = 0; it < iters; it++) Q = orth(Q.map(mul));
|
| 55 |
+
const GQ = Q.map(mul), T = new Float64Array(m * m); // Rayleigh-Ritz on the block
|
| 56 |
+
for (let a = 0; a < m; a++) for (let b = 0; b < m; b++) { let s = 0; for (let i = 0; i < n; i++) s += Q[a][i] * GQ[b][i]; T[a * m + b] = s; }
|
| 57 |
+
for (let a = 0; a < m; a++) for (let b = 0; b < a; b++) T[a * m + b] = T[b * m + a] = (T[a * m + b] + T[b * m + a]) / 2;
|
| 58 |
+
const { vals, V } = jacobiEig(T, m);
|
| 59 |
+
const order = vals.map((v, i) => [v, i]).sort((x, y) => y[0] - x[0]).slice(0, k).map((x) => x[1]);
|
| 60 |
+
return order.map((col) => {
|
| 61 |
+
const u = new Float64Array(n);
|
| 62 |
+
for (let a = 0; a < m; a++) { const w = V[a * m + col]; for (let i = 0; i < n; i++) u[i] += w * Q[a][i]; }
|
| 63 |
+
const v = new Float64Array(d);
|
| 64 |
+
for (let i = 0; i < n; i++) { const ui = u[i], ri = i * d; if (ui) for (let a = 0; a < d; a++) v[a] += ui * X[ri + a]; }
|
| 65 |
+
let nrm = 0; for (let a = 0; a < d; a++) nrm += v[a] * v[a]; nrm = Math.sqrt(nrm) || 1;
|
| 66 |
+
let piv = 0;
|
| 67 |
+
for (let a = 0; a < d; a++) { v[a] /= nrm; if (Math.abs(v[a]) > Math.abs(v[piv])) piv = a; }
|
| 68 |
+
if (v[piv] < 0) for (let a = 0; a < d; a++) v[a] = -v[a];
|
| 69 |
+
return v;
|
| 70 |
+
});
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
function quantile(sorted, q) { // torch.quantile, linear interpolation
|
| 74 |
+
const pos = (sorted.length - 1) * q, lo = Math.floor(pos), hi = Math.ceil(pos);
|
| 75 |
+
return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
export class ThinkerView {
|
| 79 |
+
constructor(planCanvas, predCanvas) { this.planCanvas = planCanvas; this.predCanvas = predCanvas; this.reset(); }
|
| 80 |
+
|
| 81 |
+
reset() { this.basis = null; this.lo = null; this.hi = null; }
|
| 82 |
+
|
| 83 |
+
// plan: Float32Array (C * g * g), channel-major (1, C, g, g)
|
| 84 |
+
drawPlan(plan, C = 640, g = 16) {
|
| 85 |
+
const n = g * g, X = new Float64Array(n * C);
|
| 86 |
+
for (let c = 0; c < C; c++) for (let p = 0; p < n; p++) X[p * C + c] = plan[c * n + p];
|
| 87 |
+
for (let c = 0; c < C; c++) { let m = 0; for (let p = 0; p < n; p++) m += X[p * C + c]; m /= n; for (let p = 0; p < n; p++) X[p * C + c] -= m; }
|
| 88 |
+
if (!this.basis) this.basis = topComponents(X, n, C);
|
| 89 |
+
const Y = this.basis.map((v) => { const y = new Float64Array(n); for (let p = 0; p < n; p++) { let s = 0; const r = p * C; for (let c = 0; c < C; c++) s += X[r + c] * v[c]; y[p] = s; } return y; });
|
| 90 |
+
if (!this.lo) {
|
| 91 |
+
this.lo = Y.map((y) => quantile(Float64Array.from(y).sort(), 0.02));
|
| 92 |
+
this.hi = Y.map((y) => quantile(Float64Array.from(y).sort(), 0.98));
|
| 93 |
+
}
|
| 94 |
+
const img = new ImageData(g, g);
|
| 95 |
+
for (let p = 0; p < n; p++) {
|
| 96 |
+
for (let k = 0; k < 3; k++) img.data[p * 4 + k] = Math.round(Math.min(1, Math.max(0, (Y[k][p] - this.lo[k]) / (this.hi[k] - this.lo[k] + 1e-8))) * 255);
|
| 97 |
+
img.data[p * 4 + 3] = 255;
|
| 98 |
+
}
|
| 99 |
+
const cv = this.planCanvas; cv.width = g; cv.height = g;
|
| 100 |
+
cv.getContext("2d").putImageData(img, 0, 0);
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
// x1: Float32Array (4 * hw * hw) in the model's (scaled SD-VAE) latent space
|
| 104 |
+
drawPrediction(x1, hw) {
|
| 105 |
+
const n = hw * hw, img = new ImageData(hw, hw);
|
| 106 |
+
for (let p = 0; p < n; p++) {
|
| 107 |
+
for (let k = 0; k < 3; k++) {
|
| 108 |
+
let s = 0; for (let c = 0; c < 4; c++) s += x1[c * n + p] * RGB_FACTORS[c][k];
|
| 109 |
+
img.data[p * 4 + k] = Math.floor(Math.min(1, Math.max(0, (s + 1) / 2)) * 255);
|
| 110 |
+
}
|
| 111 |
+
img.data[p * 4 + 3] = 255;
|
| 112 |
+
}
|
| 113 |
+
const cv = this.predCanvas; cv.width = hw; cv.height = hw;
|
| 114 |
+
cv.getContext("2d").putImageData(img, 0, 0);
|
| 115 |
+
}
|
| 116 |
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"prompt": "a green teapot and a red cup on a table",
|
| 3 |
+
"steps": 50,
|
| 4 |
+
"cfg": 3.0,
|
| 5 |
+
"seed": 0,
|
| 6 |
+
"source": "release AgatePipeline, CPU fp32, fast_vae (TAESD), torch.randn replaced by parity_noise.bin",
|
| 7 |
+
"onnx_cpu_latent_max_abs": 0.10324227809906006,
|
| 8 |
+
"onnx_cpu_image_max_abs": 14
|
| 9 |
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:91cbf77d3ba00b0a6517adc736296ebefbd5fd69070d76ddacfedff32d461b1b
|
| 3 |
+
size 196608
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:de43bcd6e7416d9bcb997b22da8c19d5401a07c0fa651185ec6d49372dba5f89
|
| 3 |
+
size 16384
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3deff7f51dd573fad149014c76a44c4b6f6251fbaaa9b8e78b794e805871773f
|
| 3 |
+
size 16384
|
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"prompt": "a shop sign that says \"OPEN\", three red apples, no people",
|
| 3 |
+
"prepared": "a shop sign that says \"OPEN\", three red apples || spell: O P E N",
|
| 4 |
+
"negative": "people",
|
| 5 |
+
"counts_nonzero": {
|
| 6 |
+
"9": 3.0
|
| 7 |
+
},
|
| 8 |
+
"resolution": 256,
|
| 9 |
+
"steps": 50,
|
| 10 |
+
"cfg": 3.0,
|
| 11 |
+
"seed": 0,
|
| 12 |
+
"shift": 1.0,
|
| 13 |
+
"source": "release AgatePipeline (003), CPU fp32, fast_vae (TAESD), watermark off, torch.randn replaced by parity_noise.bin",
|
| 14 |
+
"onnx_cpu_latent_max_abs": 0.1917351484298706,
|
| 15 |
+
"onnx_cpu_image_max_abs": 47,
|
| 16 |
+
"onnx_cpu_image_mean_abs": 0.21456400553385416,
|
| 17 |
+
"onnx_cpu_psnr_db": 48.9241802774759
|
| 18 |
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ded558a14b1d2af78f387ec7c8f807dfaa0b82bc298c6519f4976e4f1a406260
|
| 3 |
+
size 196608
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3d68c5f46abf7b82ca9bcabd397a0765c52ae28c2e3443583c78ee115005d2bb
|
| 3 |
+
size 16384
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3deff7f51dd573fad149014c76a44c4b6f6251fbaaa9b8e78b794e805871773f
|
| 3 |
+
size 16384
|
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"prompt": "a shop sign that says \"OPEN\", three red apples, no people",
|
| 3 |
+
"prepared": "a shop sign that says \"OPEN\", three red apples || spell: O P E N",
|
| 4 |
+
"negative": "people",
|
| 5 |
+
"counts_nonzero": {
|
| 6 |
+
"9": 3.0
|
| 7 |
+
},
|
| 8 |
+
"resolution": 512,
|
| 9 |
+
"steps": 50,
|
| 10 |
+
"cfg": 3.0,
|
| 11 |
+
"seed": 0,
|
| 12 |
+
"shift": 2.0,
|
| 13 |
+
"source": "release AgatePipeline (003), CPU fp32, fast_vae (TAESD), watermark off, torch.randn replaced by parity_noise.bin",
|
| 14 |
+
"onnx_cpu_latent_max_abs": 0.11000287532806396,
|
| 15 |
+
"onnx_cpu_image_max_abs": 28,
|
| 16 |
+
"onnx_cpu_image_mean_abs": 0.11280314127604167,
|
| 17 |
+
"onnx_cpu_psnr_db": 54.79136477697858
|
| 18 |
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fafe740a618a3a4343bfe8ddf9819f353f9064004b8dd06c9676bd70f05c1621
|
| 3 |
+
size 786432
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9627da8e372d20fd5d2f60166d61eebc6ad104a29f17dd2d96aae6a0c917a204
|
| 3 |
+
size 65536
|
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b60eb3b5b14ff459a5517d7921ec74e7f82a23caaf9d5894e5e4a3f16ee9fd40
|
| 3 |
+
size 65536
|
|
@@ -0,0 +1,1251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"source": "agate-preview-003/agate (prompt_norm.py, AgatePipeline.prepare)",
|
| 3 |
+
"cases": [
|
| 4 |
+
{
|
| 5 |
+
"prompt": "a red cube on top of a blue sphere",
|
| 6 |
+
"prepared": "a red cube on top of a blue sphere",
|
| 7 |
+
"negative": "",
|
| 8 |
+
"ids": [
|
| 9 |
+
50281,
|
| 10 |
+
66,
|
| 11 |
+
2502,
|
| 12 |
+
23636,
|
| 13 |
+
327,
|
| 14 |
+
1755,
|
| 15 |
+
273,
|
| 16 |
+
247,
|
| 17 |
+
4797,
|
| 18 |
+
15269,
|
| 19 |
+
50282
|
| 20 |
+
],
|
| 21 |
+
"neg_ids": [
|
| 22 |
+
50281,
|
| 23 |
+
50282
|
| 24 |
+
],
|
| 25 |
+
"counts": {},
|
| 26 |
+
"find_counts": []
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"prompt": "a green teapot and a red cup on a table",
|
| 30 |
+
"prepared": "a green teapot and a red cup on a table",
|
| 31 |
+
"negative": "",
|
| 32 |
+
"ids": [
|
| 33 |
+
50281,
|
| 34 |
+
66,
|
| 35 |
+
4759,
|
| 36 |
+
716,
|
| 37 |
+
522,
|
| 38 |
+
302,
|
| 39 |
+
285,
|
| 40 |
+
247,
|
| 41 |
+
2502,
|
| 42 |
+
5500,
|
| 43 |
+
327,
|
| 44 |
+
247,
|
| 45 |
+
2829,
|
| 46 |
+
50282
|
| 47 |
+
],
|
| 48 |
+
"neg_ids": [
|
| 49 |
+
50281,
|
| 50 |
+
50282
|
| 51 |
+
],
|
| 52 |
+
"counts": {},
|
| 53 |
+
"find_counts": []
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"prompt": "three cats sitting on a sofa",
|
| 57 |
+
"prepared": "three cats sitting on a sofa",
|
| 58 |
+
"negative": "",
|
| 59 |
+
"ids": [
|
| 60 |
+
50281,
|
| 61 |
+
13524,
|
| 62 |
+
16581,
|
| 63 |
+
7063,
|
| 64 |
+
327,
|
| 65 |
+
247,
|
| 66 |
+
30929,
|
| 67 |
+
50282
|
| 68 |
+
],
|
| 69 |
+
"neg_ids": [
|
| 70 |
+
50281,
|
| 71 |
+
50282
|
| 72 |
+
],
|
| 73 |
+
"counts": {
|
| 74 |
+
"1": 3.0
|
| 75 |
+
},
|
| 76 |
+
"find_counts": [
|
| 77 |
+
[
|
| 78 |
+
0,
|
| 79 |
+
5,
|
| 80 |
+
3
|
| 81 |
+
]
|
| 82 |
+
]
|
| 83 |
+
},
|
| 84 |
+
{
|
| 85 |
+
"prompt": "3 dogs and 2 birds in a park",
|
| 86 |
+
"prepared": "three dogs and two birds in a park",
|
| 87 |
+
"negative": "",
|
| 88 |
+
"ids": [
|
| 89 |
+
50281,
|
| 90 |
+
13524,
|
| 91 |
+
9097,
|
| 92 |
+
285,
|
| 93 |
+
767,
|
| 94 |
+
11260,
|
| 95 |
+
275,
|
| 96 |
+
247,
|
| 97 |
+
5603,
|
| 98 |
+
50282
|
| 99 |
+
],
|
| 100 |
+
"neg_ids": [
|
| 101 |
+
50281,
|
| 102 |
+
50282
|
| 103 |
+
],
|
| 104 |
+
"counts": {
|
| 105 |
+
"1": 3.0,
|
| 106 |
+
"4": 2.0
|
| 107 |
+
},
|
| 108 |
+
"find_counts": [
|
| 109 |
+
[
|
| 110 |
+
0,
|
| 111 |
+
5,
|
| 112 |
+
3
|
| 113 |
+
],
|
| 114 |
+
[
|
| 115 |
+
15,
|
| 116 |
+
18,
|
| 117 |
+
2
|
| 118 |
+
]
|
| 119 |
+
]
|
| 120 |
+
},
|
| 121 |
+
{
|
| 122 |
+
"prompt": "A PHOTO OF THREE RED APPLES ON A WOODEN TABLE",
|
| 123 |
+
"prepared": "a photo of three red apples on a wooden table",
|
| 124 |
+
"negative": "",
|
| 125 |
+
"ids": [
|
| 126 |
+
50281,
|
| 127 |
+
66,
|
| 128 |
+
7512,
|
| 129 |
+
273,
|
| 130 |
+
1264,
|
| 131 |
+
2502,
|
| 132 |
+
28580,
|
| 133 |
+
327,
|
| 134 |
+
247,
|
| 135 |
+
14872,
|
| 136 |
+
2829,
|
| 137 |
+
50282
|
| 138 |
+
],
|
| 139 |
+
"neg_ids": [
|
| 140 |
+
50281,
|
| 141 |
+
50282
|
| 142 |
+
],
|
| 143 |
+
"counts": {
|
| 144 |
+
"4": 3.0
|
| 145 |
+
},
|
| 146 |
+
"find_counts": [
|
| 147 |
+
[
|
| 148 |
+
11,
|
| 149 |
+
16,
|
| 150 |
+
3
|
| 151 |
+
]
|
| 152 |
+
]
|
| 153 |
+
},
|
| 154 |
+
{
|
| 155 |
+
"prompt": "A Photo Of Two Cats Sleeping On A Bed",
|
| 156 |
+
"prepared": "A photo of two cats sleeping on a bed",
|
| 157 |
+
"negative": "",
|
| 158 |
+
"ids": [
|
| 159 |
+
50281,
|
| 160 |
+
34,
|
| 161 |
+
7512,
|
| 162 |
+
273,
|
| 163 |
+
767,
|
| 164 |
+
16581,
|
| 165 |
+
14343,
|
| 166 |
+
327,
|
| 167 |
+
247,
|
| 168 |
+
3722,
|
| 169 |
+
50282
|
| 170 |
+
],
|
| 171 |
+
"neg_ids": [
|
| 172 |
+
50281,
|
| 173 |
+
50282
|
| 174 |
+
],
|
| 175 |
+
"counts": {
|
| 176 |
+
"4": 2.0
|
| 177 |
+
},
|
| 178 |
+
"find_counts": [
|
| 179 |
+
[
|
| 180 |
+
11,
|
| 181 |
+
14,
|
| 182 |
+
2
|
| 183 |
+
]
|
| 184 |
+
]
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
"prompt": "Paris at night, the Eiffel Tower lit up",
|
| 188 |
+
"prepared": "Paris at night, the Eiffel Tower lit up",
|
| 189 |
+
"negative": "",
|
| 190 |
+
"ids": [
|
| 191 |
+
50281,
|
| 192 |
+
36062,
|
| 193 |
+
387,
|
| 194 |
+
2360,
|
| 195 |
+
13,
|
| 196 |
+
253,
|
| 197 |
+
444,
|
| 198 |
+
1648,
|
| 199 |
+
293,
|
| 200 |
+
20610,
|
| 201 |
+
6195,
|
| 202 |
+
598,
|
| 203 |
+
50282
|
| 204 |
+
],
|
| 205 |
+
"neg_ids": [
|
| 206 |
+
50281,
|
| 207 |
+
50282
|
| 208 |
+
],
|
| 209 |
+
"counts": {},
|
| 210 |
+
"find_counts": []
|
| 211 |
+
},
|
| 212 |
+
{
|
| 213 |
+
"prompt": "a shop sign that says \"OPEN\", three red apples, no people",
|
| 214 |
+
"prepared": "a shop sign that says \"OPEN\", three red apples || spell: O P E N",
|
| 215 |
+
"negative": "people",
|
| 216 |
+
"ids": [
|
| 217 |
+
50281,
|
| 218 |
+
66,
|
| 219 |
+
8979,
|
| 220 |
+
861,
|
| 221 |
+
326,
|
| 222 |
+
2296,
|
| 223 |
+
346,
|
| 224 |
+
28652,
|
| 225 |
+
995,
|
| 226 |
+
1264,
|
| 227 |
+
2502,
|
| 228 |
+
28580,
|
| 229 |
+
2785,
|
| 230 |
+
15368,
|
| 231 |
+
27,
|
| 232 |
+
473,
|
| 233 |
+
367,
|
| 234 |
+
444,
|
| 235 |
+
427,
|
| 236 |
+
50282
|
| 237 |
+
],
|
| 238 |
+
"neg_ids": [
|
| 239 |
+
50281,
|
| 240 |
+
13174,
|
| 241 |
+
50282
|
| 242 |
+
],
|
| 243 |
+
"counts": {
|
| 244 |
+
"9": 3.0
|
| 245 |
+
},
|
| 246 |
+
"find_counts": [
|
| 247 |
+
[
|
| 248 |
+
30,
|
| 249 |
+
35,
|
| 250 |
+
3
|
| 251 |
+
]
|
| 252 |
+
]
|
| 253 |
+
},
|
| 254 |
+
{
|
| 255 |
+
"prompt": "a coffee cup with the text \"Good Morning\" on it",
|
| 256 |
+
"prepared": "a coffee cup with the text \"Good Morning\" on it || spell: G o o d / M o r n i n g",
|
| 257 |
+
"negative": "",
|
| 258 |
+
"ids": [
|
| 259 |
+
50281,
|
| 260 |
+
66,
|
| 261 |
+
8574,
|
| 262 |
+
5500,
|
| 263 |
+
342,
|
| 264 |
+
253,
|
| 265 |
+
2505,
|
| 266 |
+
346,
|
| 267 |
+
8620,
|
| 268 |
+
25565,
|
| 269 |
+
3,
|
| 270 |
+
327,
|
| 271 |
+
352,
|
| 272 |
+
2785,
|
| 273 |
+
15368,
|
| 274 |
+
27,
|
| 275 |
+
443,
|
| 276 |
+
258,
|
| 277 |
+
258,
|
| 278 |
+
277,
|
| 279 |
+
1227,
|
| 280 |
+
353,
|
| 281 |
+
258,
|
| 282 |
+
391,
|
| 283 |
+
295,
|
| 284 |
+
891,
|
| 285 |
+
295,
|
| 286 |
+
305,
|
| 287 |
+
50282
|
| 288 |
+
],
|
| 289 |
+
"neg_ids": [
|
| 290 |
+
50281,
|
| 291 |
+
50282
|
| 292 |
+
],
|
| 293 |
+
"counts": {},
|
| 294 |
+
"find_counts": []
|
| 295 |
+
},
|
| 296 |
+
{
|
| 297 |
+
"prompt": "a poster reading \"SALE 50% OFF\" in bold letters",
|
| 298 |
+
"prepared": "a poster reading \"SALE 50% OFF\" in bold letters || spell: S A L E / 5 0 / O F F",
|
| 299 |
+
"negative": "",
|
| 300 |
+
"ids": [
|
| 301 |
+
50281,
|
| 302 |
+
66,
|
| 303 |
+
20731,
|
| 304 |
+
4361,
|
| 305 |
+
346,
|
| 306 |
+
52,
|
| 307 |
+
23502,
|
| 308 |
+
2456,
|
| 309 |
+
6,
|
| 310 |
+
21890,
|
| 311 |
+
3,
|
| 312 |
+
275,
|
| 313 |
+
13433,
|
| 314 |
+
4876,
|
| 315 |
+
2785,
|
| 316 |
+
15368,
|
| 317 |
+
27,
|
| 318 |
+
322,
|
| 319 |
+
329,
|
| 320 |
+
418,
|
| 321 |
+
444,
|
| 322 |
+
1227,
|
| 323 |
+
608,
|
| 324 |
+
470,
|
| 325 |
+
1227,
|
| 326 |
+
473,
|
| 327 |
+
401,
|
| 328 |
+
401,
|
| 329 |
+
50282
|
| 330 |
+
],
|
| 331 |
+
"neg_ids": [
|
| 332 |
+
50281,
|
| 333 |
+
50282
|
| 334 |
+
],
|
| 335 |
+
"counts": {},
|
| 336 |
+
"find_counts": []
|
| 337 |
+
},
|
| 338 |
+
{
|
| 339 |
+
"prompt": "a neon sign “Luma Studio” on a brick wall",
|
| 340 |
+
"prepared": "a neon sign “Luma Studio” on a brick wall || spell: L u m a / S t u d i o",
|
| 341 |
+
"negative": "",
|
| 342 |
+
"ids": [
|
| 343 |
+
50281,
|
| 344 |
+
66,
|
| 345 |
+
16772,
|
| 346 |
+
861,
|
| 347 |
+
773,
|
| 348 |
+
45,
|
| 349 |
+
9307,
|
| 350 |
+
17203,
|
| 351 |
+
668,
|
| 352 |
+
327,
|
| 353 |
+
247,
|
| 354 |
+
20098,
|
| 355 |
+
3402,
|
| 356 |
+
2785,
|
| 357 |
+
15368,
|
| 358 |
+
27,
|
| 359 |
+
418,
|
| 360 |
+
1484,
|
| 361 |
+
278,
|
| 362 |
+
247,
|
| 363 |
+
1227,
|
| 364 |
+
322,
|
| 365 |
+
246,
|
| 366 |
+
1484,
|
| 367 |
+
277,
|
| 368 |
+
891,
|
| 369 |
+
258,
|
| 370 |
+
50282
|
| 371 |
+
],
|
| 372 |
+
"neg_ids": [
|
| 373 |
+
50281,
|
| 374 |
+
50282
|
| 375 |
+
],
|
| 376 |
+
"counts": {},
|
| 377 |
+
"find_counts": []
|
| 378 |
+
},
|
| 379 |
+
{
|
| 380 |
+
"prompt": "a book cover titled \"The Old Man and the Sea\"",
|
| 381 |
+
"prepared": "a book cover titled \"The Old Man and the Sea\" || spell: T h e / O l d / M a n / a n d / t h e / S e a",
|
| 382 |
+
"negative": "",
|
| 383 |
+
"ids": [
|
| 384 |
+
50281,
|
| 385 |
+
66,
|
| 386 |
+
1984,
|
| 387 |
+
3835,
|
| 388 |
+
18879,
|
| 389 |
+
346,
|
| 390 |
+
510,
|
| 391 |
+
8937,
|
| 392 |
+
3083,
|
| 393 |
+
285,
|
| 394 |
+
253,
|
| 395 |
+
11936,
|
| 396 |
+
3,
|
| 397 |
+
2785,
|
| 398 |
+
15368,
|
| 399 |
+
27,
|
| 400 |
+
308,
|
| 401 |
+
288,
|
| 402 |
+
299,
|
| 403 |
+
1227,
|
| 404 |
+
473,
|
| 405 |
+
298,
|
| 406 |
+
277,
|
| 407 |
+
1227,
|
| 408 |
+
353,
|
| 409 |
+
247,
|
| 410 |
+
295,
|
| 411 |
+
1227,
|
| 412 |
+
247,
|
| 413 |
+
295,
|
| 414 |
+
277,
|
| 415 |
+
1227,
|
| 416 |
+
246,
|
| 417 |
+
288,
|
| 418 |
+
299,
|
| 419 |
+
1227,
|
| 420 |
+
322,
|
| 421 |
+
299,
|
| 422 |
+
247,
|
| 423 |
+
50282
|
| 424 |
+
],
|
| 425 |
+
"neg_ids": [
|
| 426 |
+
50281,
|
| 427 |
+
50282
|
| 428 |
+
],
|
| 429 |
+
"counts": {},
|
| 430 |
+
"find_counts": []
|
| 431 |
+
},
|
| 432 |
+
{
|
| 433 |
+
"prompt": "a logo for \"Q&A Labs!\" with a fox, without text shadow",
|
| 434 |
+
"prepared": "a logo for \"Q&A Labs!\" with a fox || spell: Q & A / L a b s !",
|
| 435 |
+
"negative": "text shadow",
|
| 436 |
+
"ids": [
|
| 437 |
+
50281,
|
| 438 |
+
66,
|
| 439 |
+
18479,
|
| 440 |
+
323,
|
| 441 |
+
346,
|
| 442 |
+
50,
|
| 443 |
+
7,
|
| 444 |
+
34,
|
| 445 |
+
40840,
|
| 446 |
+
1476,
|
| 447 |
+
342,
|
| 448 |
+
247,
|
| 449 |
+
30013,
|
| 450 |
+
2785,
|
| 451 |
+
15368,
|
| 452 |
+
27,
|
| 453 |
+
1165,
|
| 454 |
+
708,
|
| 455 |
+
329,
|
| 456 |
+
1227,
|
| 457 |
+
418,
|
| 458 |
+
247,
|
| 459 |
+
270,
|
| 460 |
+
256,
|
| 461 |
+
2195,
|
| 462 |
+
50282
|
| 463 |
+
],
|
| 464 |
+
"neg_ids": [
|
| 465 |
+
50281,
|
| 466 |
+
1156,
|
| 467 |
+
12195,
|
| 468 |
+
50282
|
| 469 |
+
],
|
| 470 |
+
"counts": {},
|
| 471 |
+
"find_counts": []
|
| 472 |
+
},
|
| 473 |
+
{
|
| 474 |
+
"prompt": "a landscape without any people and no buildings",
|
| 475 |
+
"prepared": "a landscape and",
|
| 476 |
+
"negative": "people, buildings",
|
| 477 |
+
"ids": [
|
| 478 |
+
50281,
|
| 479 |
+
66,
|
| 480 |
+
13016,
|
| 481 |
+
285,
|
| 482 |
+
50282
|
| 483 |
+
],
|
| 484 |
+
"neg_ids": [
|
| 485 |
+
50281,
|
| 486 |
+
13174,
|
| 487 |
+
13,
|
| 488 |
+
9195,
|
| 489 |
+
50282
|
| 490 |
+
],
|
| 491 |
+
"counts": {},
|
| 492 |
+
"find_counts": []
|
| 493 |
+
},
|
| 494 |
+
{
|
| 495 |
+
"prompt": "a bowl of fruit with no bananas",
|
| 496 |
+
"prepared": "a bowl of fruit",
|
| 497 |
+
"negative": "bananas",
|
| 498 |
+
"ids": [
|
| 499 |
+
50281,
|
| 500 |
+
66,
|
| 501 |
+
11636,
|
| 502 |
+
273,
|
| 503 |
+
9279,
|
| 504 |
+
50282
|
| 505 |
+
],
|
| 506 |
+
"neg_ids": [
|
| 507 |
+
50281,
|
| 508 |
+
5568,
|
| 509 |
+
24288,
|
| 510 |
+
50282
|
| 511 |
+
],
|
| 512 |
+
"counts": {},
|
| 513 |
+
"find_counts": []
|
| 514 |
+
},
|
| 515 |
+
{
|
| 516 |
+
"prompt": "a cat, no dog, and a mouse",
|
| 517 |
+
"prepared": "a cat, and a mouse",
|
| 518 |
+
"negative": "dog",
|
| 519 |
+
"ids": [
|
| 520 |
+
50281,
|
| 521 |
+
66,
|
| 522 |
+
5798,
|
| 523 |
+
13,
|
| 524 |
+
285,
|
| 525 |
+
247,
|
| 526 |
+
6521,
|
| 527 |
+
50282
|
| 528 |
+
],
|
| 529 |
+
"neg_ids": [
|
| 530 |
+
50281,
|
| 531 |
+
21428,
|
| 532 |
+
50282
|
| 533 |
+
],
|
| 534 |
+
"counts": {},
|
| 535 |
+
"find_counts": []
|
| 536 |
+
},
|
| 537 |
+
{
|
| 538 |
+
"prompt": "the one and only unicorn",
|
| 539 |
+
"prepared": "the one and only unicorn",
|
| 540 |
+
"negative": "",
|
| 541 |
+
"ids": [
|
| 542 |
+
50281,
|
| 543 |
+
783,
|
| 544 |
+
581,
|
| 545 |
+
285,
|
| 546 |
+
760,
|
| 547 |
+
440,
|
| 548 |
+
41257,
|
| 549 |
+
50282
|
| 550 |
+
],
|
| 551 |
+
"neg_ids": [
|
| 552 |
+
50281,
|
| 553 |
+
50282
|
| 554 |
+
],
|
| 555 |
+
"counts": {},
|
| 556 |
+
"find_counts": []
|
| 557 |
+
},
|
| 558 |
+
{
|
| 559 |
+
"prompt": "no one is here, an empty street",
|
| 560 |
+
"prepared": "an empty street",
|
| 561 |
+
"negative": "one is here",
|
| 562 |
+
"ids": [
|
| 563 |
+
50281,
|
| 564 |
+
266,
|
| 565 |
+
6325,
|
| 566 |
+
6406,
|
| 567 |
+
50282
|
| 568 |
+
],
|
| 569 |
+
"neg_ids": [
|
| 570 |
+
50281,
|
| 571 |
+
531,
|
| 572 |
+
310,
|
| 573 |
+
1060,
|
| 574 |
+
50282
|
| 575 |
+
],
|
| 576 |
+
"counts": {},
|
| 577 |
+
"find_counts": []
|
| 578 |
+
},
|
| 579 |
+
{
|
| 580 |
+
"prompt": "a dozen eggs in a carton",
|
| 581 |
+
"prepared": "a dozen eggs in a carton",
|
| 582 |
+
"negative": "",
|
| 583 |
+
"ids": [
|
| 584 |
+
50281,
|
| 585 |
+
66,
|
| 586 |
+
13365,
|
| 587 |
+
11624,
|
| 588 |
+
275,
|
| 589 |
+
247,
|
| 590 |
+
7281,
|
| 591 |
+
251,
|
| 592 |
+
50282
|
| 593 |
+
],
|
| 594 |
+
"neg_ids": [
|
| 595 |
+
50281,
|
| 596 |
+
50282
|
| 597 |
+
],
|
| 598 |
+
"counts": {
|
| 599 |
+
"2": 12.0
|
| 600 |
+
},
|
| 601 |
+
"find_counts": [
|
| 602 |
+
[
|
| 603 |
+
2,
|
| 604 |
+
7,
|
| 605 |
+
12
|
| 606 |
+
]
|
| 607 |
+
]
|
| 608 |
+
},
|
| 609 |
+
{
|
| 610 |
+
"prompt": "a pair of shoes on the floor",
|
| 611 |
+
"prepared": "a pair of shoes on the floor",
|
| 612 |
+
"negative": "",
|
| 613 |
+
"ids": [
|
| 614 |
+
50281,
|
| 615 |
+
66,
|
| 616 |
+
4667,
|
| 617 |
+
273,
|
| 618 |
+
12682,
|
| 619 |
+
327,
|
| 620 |
+
253,
|
| 621 |
+
5254,
|
| 622 |
+
50282
|
| 623 |
+
],
|
| 624 |
+
"neg_ids": [
|
| 625 |
+
50281,
|
| 626 |
+
50282
|
| 627 |
+
],
|
| 628 |
+
"counts": {
|
| 629 |
+
"2": 2.0
|
| 630 |
+
},
|
| 631 |
+
"find_counts": [
|
| 632 |
+
[
|
| 633 |
+
2,
|
| 634 |
+
6,
|
| 635 |
+
2
|
| 636 |
+
]
|
| 637 |
+
]
|
| 638 |
+
},
|
| 639 |
+
{
|
| 640 |
+
"prompt": "a clock showing 3 pm on a wall",
|
| 641 |
+
"prepared": "a clock showing 3 pm on a wall",
|
| 642 |
+
"negative": "",
|
| 643 |
+
"ids": [
|
| 644 |
+
50281,
|
| 645 |
+
66,
|
| 646 |
+
8886,
|
| 647 |
+
4645,
|
| 648 |
+
495,
|
| 649 |
+
12920,
|
| 650 |
+
327,
|
| 651 |
+
247,
|
| 652 |
+
3402,
|
| 653 |
+
50282
|
| 654 |
+
],
|
| 655 |
+
"neg_ids": [
|
| 656 |
+
50281,
|
| 657 |
+
50282
|
| 658 |
+
],
|
| 659 |
+
"counts": {},
|
| 660 |
+
"find_counts": []
|
| 661 |
+
},
|
| 662 |
+
{
|
| 663 |
+
"prompt": "24 hours of rain over 5 houses",
|
| 664 |
+
"prepared": "24 hours of rain over five houses",
|
| 665 |
+
"negative": "",
|
| 666 |
+
"ids": [
|
| 667 |
+
50281,
|
| 668 |
+
1348,
|
| 669 |
+
3038,
|
| 670 |
+
273,
|
| 671 |
+
9313,
|
| 672 |
+
689,
|
| 673 |
+
2620,
|
| 674 |
+
9910,
|
| 675 |
+
50282
|
| 676 |
+
],
|
| 677 |
+
"neg_ids": [
|
| 678 |
+
50281,
|
| 679 |
+
50282
|
| 680 |
+
],
|
| 681 |
+
"counts": {
|
| 682 |
+
"6": 5.0
|
| 683 |
+
},
|
| 684 |
+
"find_counts": [
|
| 685 |
+
[
|
| 686 |
+
22,
|
| 687 |
+
26,
|
| 688 |
+
5
|
| 689 |
+
]
|
| 690 |
+
]
|
| 691 |
+
},
|
| 692 |
+
{
|
| 693 |
+
"prompt": "twelve roses in a\tvase\n",
|
| 694 |
+
"prepared": "twelve roses in a vase",
|
| 695 |
+
"negative": "",
|
| 696 |
+
"ids": [
|
| 697 |
+
50281,
|
| 698 |
+
7553,
|
| 699 |
+
11071,
|
| 700 |
+
38910,
|
| 701 |
+
275,
|
| 702 |
+
247,
|
| 703 |
+
362,
|
| 704 |
+
511,
|
| 705 |
+
50282
|
| 706 |
+
],
|
| 707 |
+
"neg_ids": [
|
| 708 |
+
50281,
|
| 709 |
+
50282
|
| 710 |
+
],
|
| 711 |
+
"counts": {
|
| 712 |
+
"1": 12.0,
|
| 713 |
+
"2": 12.0
|
| 714 |
+
},
|
| 715 |
+
"find_counts": [
|
| 716 |
+
[
|
| 717 |
+
0,
|
| 718 |
+
6,
|
| 719 |
+
12
|
| 720 |
+
]
|
| 721 |
+
]
|
| 722 |
+
},
|
| 723 |
+
{
|
| 724 |
+
"prompt": "a cake with 7 candles and 21 balloons",
|
| 725 |
+
"prepared": "a cake with seven candles and 21 balloons",
|
| 726 |
+
"negative": "",
|
| 727 |
+
"ids": [
|
| 728 |
+
50281,
|
| 729 |
+
66,
|
| 730 |
+
15221,
|
| 731 |
+
342,
|
| 732 |
+
5093,
|
| 733 |
+
36555,
|
| 734 |
+
285,
|
| 735 |
+
3127,
|
| 736 |
+
50210,
|
| 737 |
+
50282
|
| 738 |
+
],
|
| 739 |
+
"neg_ids": [
|
| 740 |
+
50281,
|
| 741 |
+
50282
|
| 742 |
+
],
|
| 743 |
+
"counts": {
|
| 744 |
+
"4": 7.0,
|
| 745 |
+
"7": 21.0
|
| 746 |
+
},
|
| 747 |
+
"find_counts": [
|
| 748 |
+
[
|
| 749 |
+
12,
|
| 750 |
+
17,
|
| 751 |
+
7
|
| 752 |
+
],
|
| 753 |
+
[
|
| 754 |
+
30,
|
| 755 |
+
32,
|
| 756 |
+
21
|
| 757 |
+
]
|
| 758 |
+
]
|
| 759 |
+
},
|
| 760 |
+
{
|
| 761 |
+
"prompt": "one apple, two pears and four plums",
|
| 762 |
+
"prepared": "one apple, two pears and four plums",
|
| 763 |
+
"negative": "",
|
| 764 |
+
"ids": [
|
| 765 |
+
50281,
|
| 766 |
+
531,
|
| 767 |
+
19126,
|
| 768 |
+
13,
|
| 769 |
+
767,
|
| 770 |
+
268,
|
| 771 |
+
6364,
|
| 772 |
+
285,
|
| 773 |
+
1740,
|
| 774 |
+
499,
|
| 775 |
+
7640,
|
| 776 |
+
50282
|
| 777 |
+
],
|
| 778 |
+
"neg_ids": [
|
| 779 |
+
50281,
|
| 780 |
+
50282
|
| 781 |
+
],
|
| 782 |
+
"counts": {
|
| 783 |
+
"1": 1.0,
|
| 784 |
+
"4": 2.0,
|
| 785 |
+
"8": 4.0
|
| 786 |
+
},
|
| 787 |
+
"find_counts": [
|
| 788 |
+
[
|
| 789 |
+
0,
|
| 790 |
+
3,
|
| 791 |
+
1
|
| 792 |
+
],
|
| 793 |
+
[
|
| 794 |
+
11,
|
| 795 |
+
14,
|
| 796 |
+
2
|
| 797 |
+
],
|
| 798 |
+
[
|
| 799 |
+
25,
|
| 800 |
+
29,
|
| 801 |
+
4
|
| 802 |
+
]
|
| 803 |
+
]
|
| 804 |
+
},
|
| 805 |
+
{
|
| 806 |
+
"prompt": " leading and trailing spaces ",
|
| 807 |
+
"prepared": "leading and trailing spaces",
|
| 808 |
+
"negative": "",
|
| 809 |
+
"ids": [
|
| 810 |
+
50281,
|
| 811 |
+
16378,
|
| 812 |
+
285,
|
| 813 |
+
28417,
|
| 814 |
+
8470,
|
| 815 |
+
50282
|
| 816 |
+
],
|
| 817 |
+
"neg_ids": [
|
| 818 |
+
50281,
|
| 819 |
+
50282
|
| 820 |
+
],
|
| 821 |
+
"counts": {},
|
| 822 |
+
"find_counts": []
|
| 823 |
+
},
|
| 824 |
+
{
|
| 825 |
+
"prompt": "{\"high_level_description\": \"a flat logo\", \"text\": \"no cast shadow\"}",
|
| 826 |
+
"prepared": "{\"high_level_description\": \"a flat logo\", \"text\": \"no cast shadow\"}",
|
| 827 |
+
"negative": "",
|
| 828 |
+
"ids": [
|
| 829 |
+
50281,
|
| 830 |
+
9819,
|
| 831 |
+
8656,
|
| 832 |
+
64,
|
| 833 |
+
5251,
|
| 834 |
+
64,
|
| 835 |
+
10008,
|
| 836 |
+
1381,
|
| 837 |
+
346,
|
| 838 |
+
66,
|
| 839 |
+
6507,
|
| 840 |
+
18479,
|
| 841 |
+
995,
|
| 842 |
+
346,
|
| 843 |
+
1156,
|
| 844 |
+
1381,
|
| 845 |
+
346,
|
| 846 |
+
2369,
|
| 847 |
+
5248,
|
| 848 |
+
12195,
|
| 849 |
+
986,
|
| 850 |
+
50282
|
| 851 |
+
],
|
| 852 |
+
"neg_ids": [
|
| 853 |
+
50281,
|
| 854 |
+
50282
|
| 855 |
+
],
|
| 856 |
+
"counts": {},
|
| 857 |
+
"find_counts": []
|
| 858 |
+
},
|
| 859 |
+
{
|
| 860 |
+
"prompt": "a sign \"unclosed quote with three words",
|
| 861 |
+
"prepared": "a sign \"unclosed quote with three words",
|
| 862 |
+
"negative": "",
|
| 863 |
+
"ids": [
|
| 864 |
+
50281,
|
| 865 |
+
66,
|
| 866 |
+
861,
|
| 867 |
+
346,
|
| 868 |
+
328,
|
| 869 |
+
13784,
|
| 870 |
+
14430,
|
| 871 |
+
342,
|
| 872 |
+
1264,
|
| 873 |
+
3000,
|
| 874 |
+
50282
|
| 875 |
+
],
|
| 876 |
+
"neg_ids": [
|
| 877 |
+
50281,
|
| 878 |
+
50282
|
| 879 |
+
],
|
| 880 |
+
"counts": {
|
| 881 |
+
"8": 3.0
|
| 882 |
+
},
|
| 883 |
+
"find_counts": [
|
| 884 |
+
[
|
| 885 |
+
28,
|
| 886 |
+
33,
|
| 887 |
+
3
|
| 888 |
+
]
|
| 889 |
+
]
|
| 890 |
+
},
|
| 891 |
+
{
|
| 892 |
+
"prompt": "a sticker that says \"hi\" and a sticker that says \"bye\" and one that says \"ok\" and \"four\"",
|
| 893 |
+
"prepared": "a sticker that says \"hi\" and a sticker that says \"bye\" and one that says \"ok\" and \"four\" || spell: h i ; b y e ; o k",
|
| 894 |
+
"negative": "",
|
| 895 |
+
"ids": [
|
| 896 |
+
50281,
|
| 897 |
+
66,
|
| 898 |
+
7356,
|
| 899 |
+
254,
|
| 900 |
+
326,
|
| 901 |
+
2296,
|
| 902 |
+
346,
|
| 903 |
+
5801,
|
| 904 |
+
3,
|
| 905 |
+
285,
|
| 906 |
+
247,
|
| 907 |
+
7356,
|
| 908 |
+
254,
|
| 909 |
+
326,
|
| 910 |
+
2296,
|
| 911 |
+
346,
|
| 912 |
+
17034,
|
| 913 |
+
3,
|
| 914 |
+
285,
|
| 915 |
+
581,
|
| 916 |
+
326,
|
| 917 |
+
2296,
|
| 918 |
+
346,
|
| 919 |
+
536,
|
| 920 |
+
3,
|
| 921 |
+
285,
|
| 922 |
+
346,
|
| 923 |
+
12496,
|
| 924 |
+
3,
|
| 925 |
+
2785,
|
| 926 |
+
15368,
|
| 927 |
+
27,
|
| 928 |
+
288,
|
| 929 |
+
891,
|
| 930 |
+
3706,
|
| 931 |
+
270,
|
| 932 |
+
340,
|
| 933 |
+
299,
|
| 934 |
+
3706,
|
| 935 |
+
258,
|
| 936 |
+
465,
|
| 937 |
+
50282
|
| 938 |
+
],
|
| 939 |
+
"neg_ids": [
|
| 940 |
+
50281,
|
| 941 |
+
50282
|
| 942 |
+
],
|
| 943 |
+
"counts": {
|
| 944 |
+
"19": 1.0
|
| 945 |
+
},
|
| 946 |
+
"find_counts": [
|
| 947 |
+
[
|
| 948 |
+
59,
|
| 949 |
+
62,
|
| 950 |
+
1
|
| 951 |
+
]
|
| 952 |
+
]
|
| 953 |
+
},
|
| 954 |
+
{
|
| 955 |
+
"prompt": "a café in Zürich with 2 bicycles",
|
| 956 |
+
"prepared": "a café in Zürich with two bicycles",
|
| 957 |
+
"negative": "",
|
| 958 |
+
"ids": [
|
| 959 |
+
50281,
|
| 960 |
+
66,
|
| 961 |
+
36241,
|
| 962 |
+
275,
|
| 963 |
+
1503,
|
| 964 |
+
3090,
|
| 965 |
+
5969,
|
| 966 |
+
342,
|
| 967 |
+
767,
|
| 968 |
+
19151,
|
| 969 |
+
5005,
|
| 970 |
+
50282
|
| 971 |
+
],
|
| 972 |
+
"neg_ids": [
|
| 973 |
+
50281,
|
| 974 |
+
50282
|
| 975 |
+
],
|
| 976 |
+
"counts": {
|
| 977 |
+
"8": 2.0
|
| 978 |
+
},
|
| 979 |
+
"find_counts": [
|
| 980 |
+
[
|
| 981 |
+
22,
|
| 982 |
+
25,
|
| 983 |
+
2
|
| 984 |
+
]
|
| 985 |
+
]
|
| 986 |
+
},
|
| 987 |
+
{
|
| 988 |
+
"prompt": "an emoji-style icon of a rocket 🚀 with three stars",
|
| 989 |
+
"prepared": "an emoji-style icon of a rocket 🚀 with three stars",
|
| 990 |
+
"negative": "",
|
| 991 |
+
"ids": [
|
| 992 |
+
50281,
|
| 993 |
+
266,
|
| 994 |
+
802,
|
| 995 |
+
80,
|
| 996 |
+
8020,
|
| 997 |
+
14,
|
| 998 |
+
4826,
|
| 999 |
+
10651,
|
| 1000 |
+
273,
|
| 1001 |
+
247,
|
| 1002 |
+
21912,
|
| 1003 |
+
22692,
|
| 1004 |
+
237,
|
| 1005 |
+
211,
|
| 1006 |
+
342,
|
| 1007 |
+
1264,
|
| 1008 |
+
6114,
|
| 1009 |
+
50282
|
| 1010 |
+
],
|
| 1011 |
+
"neg_ids": [
|
| 1012 |
+
50281,
|
| 1013 |
+
50282
|
| 1014 |
+
],
|
| 1015 |
+
"counts": {
|
| 1016 |
+
"15": 3.0
|
| 1017 |
+
},
|
| 1018 |
+
"find_counts": [
|
| 1019 |
+
[
|
| 1020 |
+
39,
|
| 1021 |
+
44,
|
| 1022 |
+
3
|
| 1023 |
+
]
|
| 1024 |
+
]
|
| 1025 |
+
},
|
| 1026 |
+
{
|
| 1027 |
+
"prompt": "a banner \"Ünïcödé Tëxt\" above five flags",
|
| 1028 |
+
"prepared": "a banner \"Ünïcödé Tëxt\" above five flags || spell: Ü n ï c ö d é / T ë x t",
|
| 1029 |
+
"negative": "",
|
| 1030 |
+
"ids": [
|
| 1031 |
+
50281,
|
| 1032 |
+
66,
|
| 1033 |
+
29751,
|
| 1034 |
+
346,
|
| 1035 |
+
127,
|
| 1036 |
+
239,
|
| 1037 |
+
79,
|
| 1038 |
+
12589,
|
| 1039 |
+
68,
|
| 1040 |
+
25873,
|
| 1041 |
+
860,
|
| 1042 |
+
308,
|
| 1043 |
+
10093,
|
| 1044 |
+
633,
|
| 1045 |
+
3,
|
| 1046 |
+
1840,
|
| 1047 |
+
2620,
|
| 1048 |
+
12201,
|
| 1049 |
+
2785,
|
| 1050 |
+
15368,
|
| 1051 |
+
27,
|
| 1052 |
+
37995,
|
| 1053 |
+
295,
|
| 1054 |
+
1325,
|
| 1055 |
+
109,
|
| 1056 |
+
260,
|
| 1057 |
+
16919,
|
| 1058 |
+
277,
|
| 1059 |
+
3974,
|
| 1060 |
+
1227,
|
| 1061 |
+
308,
|
| 1062 |
+
41254,
|
| 1063 |
+
1269,
|
| 1064 |
+
246,
|
| 1065 |
+
50282
|
| 1066 |
+
],
|
| 1067 |
+
"neg_ids": [
|
| 1068 |
+
50281,
|
| 1069 |
+
50282
|
| 1070 |
+
],
|
| 1071 |
+
"counts": {
|
| 1072 |
+
"16": 5.0
|
| 1073 |
+
},
|
| 1074 |
+
"find_counts": [
|
| 1075 |
+
[
|
| 1076 |
+
30,
|
| 1077 |
+
34,
|
| 1078 |
+
5
|
| 1079 |
+
]
|
| 1080 |
+
]
|
| 1081 |
+
},
|
| 1082 |
+
{
|
| 1083 |
+
"prompt": "ten ten ten",
|
| 1084 |
+
"prepared": "ten ten ten",
|
| 1085 |
+
"negative": "",
|
| 1086 |
+
"ids": [
|
| 1087 |
+
50281,
|
| 1088 |
+
1866,
|
| 1089 |
+
3578,
|
| 1090 |
+
3578,
|
| 1091 |
+
50282
|
| 1092 |
+
],
|
| 1093 |
+
"neg_ids": [
|
| 1094 |
+
50281,
|
| 1095 |
+
50282
|
| 1096 |
+
],
|
| 1097 |
+
"counts": {
|
| 1098 |
+
"1": 10.0,
|
| 1099 |
+
"2": 10.0
|
| 1100 |
+
},
|
| 1101 |
+
"find_counts": [
|
| 1102 |
+
[
|
| 1103 |
+
0,
|
| 1104 |
+
3,
|
| 1105 |
+
10
|
| 1106 |
+
],
|
| 1107 |
+
[
|
| 1108 |
+
4,
|
| 1109 |
+
7,
|
| 1110 |
+
10
|
| 1111 |
+
]
|
| 1112 |
+
]
|
| 1113 |
+
},
|
| 1114 |
+
{
|
| 1115 |
+
"prompt": "Two Red Apples And A Green Pear On A Plate",
|
| 1116 |
+
"prepared": "two red apples and a green pear on a plate",
|
| 1117 |
+
"negative": "",
|
| 1118 |
+
"ids": [
|
| 1119 |
+
50281,
|
| 1120 |
+
9389,
|
| 1121 |
+
2502,
|
| 1122 |
+
28580,
|
| 1123 |
+
285,
|
| 1124 |
+
247,
|
| 1125 |
+
4759,
|
| 1126 |
+
27887,
|
| 1127 |
+
327,
|
| 1128 |
+
247,
|
| 1129 |
+
5340,
|
| 1130 |
+
50282
|
| 1131 |
+
],
|
| 1132 |
+
"neg_ids": [
|
| 1133 |
+
50281,
|
| 1134 |
+
50282
|
| 1135 |
+
],
|
| 1136 |
+
"counts": {
|
| 1137 |
+
"1": 2.0
|
| 1138 |
+
},
|
| 1139 |
+
"find_counts": [
|
| 1140 |
+
[
|
| 1141 |
+
0,
|
| 1142 |
+
3,
|
| 1143 |
+
2
|
| 1144 |
+
]
|
| 1145 |
+
]
|
| 1146 |
+
},
|
| 1147 |
+
{
|
| 1148 |
+
"prompt": "a cat without a hat",
|
| 1149 |
+
"prepared": "a cat",
|
| 1150 |
+
"negative": "hat",
|
| 1151 |
+
"ids": [
|
| 1152 |
+
50281,
|
| 1153 |
+
66,
|
| 1154 |
+
5798,
|
| 1155 |
+
50282
|
| 1156 |
+
],
|
| 1157 |
+
"neg_ids": [
|
| 1158 |
+
50281,
|
| 1159 |
+
700,
|
| 1160 |
+
50282
|
| 1161 |
+
],
|
| 1162 |
+
"counts": {},
|
| 1163 |
+
"find_counts": []
|
| 1164 |
+
},
|
| 1165 |
+
{
|
| 1166 |
+
"prompt": "",
|
| 1167 |
+
"prepared": "",
|
| 1168 |
+
"negative": "",
|
| 1169 |
+
"ids": [
|
| 1170 |
+
50281,
|
| 1171 |
+
50282
|
| 1172 |
+
],
|
| 1173 |
+
"neg_ids": [
|
| 1174 |
+
50281,
|
| 1175 |
+
50282
|
| 1176 |
+
],
|
| 1177 |
+
"counts": {},
|
| 1178 |
+
"find_counts": []
|
| 1179 |
+
},
|
| 1180 |
+
{
|
| 1181 |
+
"prompt": "a portrait of a woman, no glasses. soft light",
|
| 1182 |
+
"prepared": "a portrait of a woman. soft light",
|
| 1183 |
+
"negative": "glasses",
|
| 1184 |
+
"ids": [
|
| 1185 |
+
50281,
|
| 1186 |
+
66,
|
| 1187 |
+
22946,
|
| 1188 |
+
273,
|
| 1189 |
+
247,
|
| 1190 |
+
3416,
|
| 1191 |
+
15,
|
| 1192 |
+
2602,
|
| 1193 |
+
1708,
|
| 1194 |
+
50282
|
| 1195 |
+
],
|
| 1196 |
+
"neg_ids": [
|
| 1197 |
+
50281,
|
| 1198 |
+
25483,
|
| 1199 |
+
265,
|
| 1200 |
+
50282
|
| 1201 |
+
],
|
| 1202 |
+
"counts": {},
|
| 1203 |
+
"find_counts": []
|
| 1204 |
+
},
|
| 1205 |
+
{
|
| 1206 |
+
"prompt": "the word \"Agate\" in red serif letters, 2 lines",
|
| 1207 |
+
"prepared": "the word \"Agate\" in red serif letters, two lines || spell: A g a t e",
|
| 1208 |
+
"negative": "",
|
| 1209 |
+
"ids": [
|
| 1210 |
+
50281,
|
| 1211 |
+
783,
|
| 1212 |
+
3159,
|
| 1213 |
+
346,
|
| 1214 |
+
8903,
|
| 1215 |
+
366,
|
| 1216 |
+
3,
|
| 1217 |
+
275,
|
| 1218 |
+
2502,
|
| 1219 |
+
1151,
|
| 1220 |
+
338,
|
| 1221 |
+
4876,
|
| 1222 |
+
13,
|
| 1223 |
+
767,
|
| 1224 |
+
3104,
|
| 1225 |
+
2785,
|
| 1226 |
+
15368,
|
| 1227 |
+
27,
|
| 1228 |
+
329,
|
| 1229 |
+
305,
|
| 1230 |
+
247,
|
| 1231 |
+
246,
|
| 1232 |
+
299,
|
| 1233 |
+
50282
|
| 1234 |
+
],
|
| 1235 |
+
"neg_ids": [
|
| 1236 |
+
50281,
|
| 1237 |
+
50282
|
| 1238 |
+
],
|
| 1239 |
+
"counts": {
|
| 1240 |
+
"13": 2.0
|
| 1241 |
+
},
|
| 1242 |
+
"find_counts": [
|
| 1243 |
+
[
|
| 1244 |
+
39,
|
| 1245 |
+
42,
|
| 1246 |
+
2
|
| 1247 |
+
]
|
| 1248 |
+
]
|
| 1249 |
+
}
|
| 1250 |
+
]
|
| 1251 |
+
}
|