Files
SlabUploader/docs/contracts/PROMPTS.md
T
grokbot_ty 88278a55e8 fix(call2): no metadata dump in long description
Long desc is Hook + Benefits (+ brief trust close) only; forbid Full specs /
Attribute list / product-name header that re-lists DECLARED ATTRIBUTES or
DIMENSION FACT BLOCK. Defensively strip trailing Category:/Length:/Board Feet:
labeled dumps in parse.
2026-09-26 10:52:53 -07:00

228 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Prompts contract
> **overnight-whole-app / Call 1 OUT:** Call 1 (vision classification) is hard-disabled.
> `POST /api/v1/slabs/{id}/inference/call1` returns **410 call1_disabled**. UI triggers
> (Review "Suggest with AI", GenerateText classify) are removed. Call 2
> (`POST .../inference/call2`, Review "Generate description") stays invokable.
> Call 1 seed text and prompt-revision APIs remain for documentation / future re-enable.
Status: SPEC · 2026-09-18.
Owns prompt storage, prompt inputs, and the two inference calls.
## Requirements impact
| Requirement | Impact on this design |
|---|---|
| METADATA-5, METADATA-10, METADATA-15, METADATA-30 | taxonomy choices and cardinality |
| METADATA-35, METADATA-40, METADATA-45, METADATA-50 | Call 1 inputs, constraints, pre-fill, precedence |
| METADATA-55, METADATA-60, METADATA-65, METADATA-70 | Call 2 output, inputs, dimension safety, inference-off path |
| METADATA-75, METADATA-80, METADATA-85, METADATA-90, METADATA-95 | non-blocking, deterministic copy, pattern selection, fact block, overlay exclusion |
| METADATA-105, METADATA-106, METADATA-107 | prompt storage, revision interface, auth |
| METADATA-66 | generation logging |
| SETTINGS-30 | brand voice and geo context |
| REVIEW-40, REVIEW-50 | editability and field limits |
## 1. Storage and revisions
- Prompts live in the database, not in text files, with all revisions retained. Storage shape is
`docs/contracts/DATA-MODEL.md` §7 (`METADATA-105`).
- Revisions are append-only. Editing saves a new revision and moves the active pointer; a revision
is never updated or deleted. Reset to default and reset to revision are pointer moves.
- Each call reads the active revision fresh, and the revision used is recorded on the call
(`METADATA-66`).
- The seed text in §6 is read once on first run and written as revision 0 for each call. It is the
only prompt content that ships with the application.
## 2. Revision interface
REST, no UI (`METADATA-106`):
1. `POST /api/v1/prompts/{call}/revisions` appends a revision and returns it.
2. `POST /api/v1/prompts/{call}/activate` moves the active pointer to a revision.
3. `GET /api/v1/prompts/{call}` returns the active prompt and the revision list.
The interface is reachable on the trusted network only until it is authenticated (`METADATA-107`).
## 3. Call 1 — vision classification
Cites METADATA-35, METADATA-40, METADATA-45, METADATA-50.
**When.** Runs automatically after a calibrated draft uploads only when `inference_enabled` is
true; also on demand or retry. With inference off it is skipped and the fields are filled by hand.
**Inputs.**
- The clean processed photos at 1024 px long edge or less.
- Closed LISTS from the synced Woo cache only: `species`, `wood_categories`, `edge_terms`,
`figure_terms`, `feature_tags` (each with id + display name). Grade, thickness, and moisture are
not sent to Call 1.
- User metadata: SKU, length, thickness.
**Constraints.**
- The model may only choose values from the supplied LISTS; it never invents categories or terms.
- Feature tags are returned as `feature_tag_ids` from the supplied list only. Display names omit a
leading `feat-` prefix; Woo remains authoritative for the tag set. No free-text observation bags.
- Grade is never suggested (owner judgment).
- Decide wood categories before edge type. Cookies and burl cuts prefer Wild edge when that term
exists in the list.
**Result fields.** `species_id` + `species_confidence`; `wood_category_ids` +
`wood_category_confidence`; `edge_type_term_id` + `edge_confidence`; `figure_term_ids` +
`figure_confidence` (multi allowed); `feature_tag_ids` + `feature_tag_confidence`. Edge is singular;
figure and feature tags are multi.
**Pre-fill.** At or above the threshold (default 0.7) the field is pre-filled and remains
editable; below it the field stays empty and is not shown as a low-confidence guess. Typed values
always win. Call 1 is assist-only and never gates publish. A runtime failure is classified
recoverable or unrecoverable for the owner.
## 4. Call 2 — content prose
**When.** User-triggered only, via the Generate text control; never automatic. The control appears
only when content generation is enabled and the endpoint is reachable.
**Inputs.**
- The clean processed photos.
- The metadata confirmed or changed in review, including the primary and secondary wood categories
and the parent-category path.
- The literal dimension fact block, exact strings for species, figure term, length, thickness,
average width, width min–max, and board feet; no length-by-width-by-thickness rectangle
assumption.
- Brand voice and geo context.
No provider thread id is used, and Call 1's raw prompt and result are not sent (`METADATA-60`).
**Constraints.**
- The model writes prose only and never invents or alters a dimension; every number comes from the
fact block (`METADATA-65`).
- The dimension-overlay image is never sent (`METADATA-95`).
**Output.** Call 2 writes **both** `short_description` (plain text, ~50 words) and
`description` (HTML). The seed carries short/long writing structure (benefit→diff→trust short;
Hook→Benefits→Specs→Trust long) and requires a JSON object
`{"short_description":"...","description":"..."}`. Brand voice and geo stay Settings-injected
at assemble time — not hardcoded in the seed. The server still assembles `name`
deterministically (`METADATA-55`, `METADATA-80`); short may also be owner-edited or rebuilt via
Create short desc. The primary wood category selects the specialized prompt (`METADATA-85`).
The owner can edit the result (`REVIEW-40`).
## 5. Fallback and templates
- With inference off, the server produces the long description so create never lacks prose
(`METADATA-70`).
- The deterministic title and short-description patterns live in `docs/contracts/CONTENT-WOO.md`
(`METADATA-80`).
- The primary wood category selects the specialized Call 2 prompt (`METADATA-85`).
- All generated text stays within the field limits in `docs/contracts/WOO-API.md` §9
(`REVIEW-50`).
## 6. Seed prompt text
Read once on first run and written as revision 0 for each call (`METADATA-105`). Authored here with
the rest of the prompt discussion; the seed is the only prompt content that ships with the
application.
The seed is the starting point for tuning, not a final text. Both calls are revised ad hoc through
the §2 interface when UAT surfaces a miss.
**`call1` seed.** Shipped text in `backend/inference.py` (`CALL1_SEED`), mirrored here for review.
You are a wood identification expert for retail lumber slabs. Judge only from the photos and the
LISTS below. Use list ids only — never invent a name or id.
Work in this order: (1) species, (2) wood categories, (3) edge type, (4) figure, (5) feature tags.
Decide wood categories before edge type — edge rules depend on product form.
For each field, pick from its list and give a confidence 0–1 (1 = certain, 0 = no idea). Prefer
null or [] over a weak guess.
Species — which tree species is this slab. Match grain, color, and figure to the species list. If
more than one entry is plausible, return only the single highest-confidence match (one id). Null
only when none are credible.
Wood categories — product form / how it will be sold (live edge, dimensional, cookies & burl
slices, turning blanks, etc.). Choose every wood_categories id that clearly fits; often one,
sometimes more. [] if none fit. Finish this field before edge type.
Edge type — the long-edge mill profile. One id from edge_terms, or null only if none fit.
Meanings (match the closest list name):
• Square / square edge — both long edges milled straight (2 milled long edges).
• Live one side / Live Edge 1 side — one long edge milled, the other natural bark or waney
(1 milled long edge).
• Live both / Live edge 2 sides — neither long edge milled; both natural (0 milled long edges).
• Wild edge — use when the three mill-count cases do not fit, including cookies and burl
cuts/slices (no conventional slab long-edge pair). Prefer Wild for those forms rather than null.
Figure — visible figure in the wood (curl, quilt, spalt, plain, burl, …). Zero or more ids from
figure_terms that clearly apply.
Feature tags — defects and character marks the store tags for shoppers (knots, checks,
inclusions, …). Zero or more ids from feature_tags. If that list is empty, return [].
Return one JSON object with exactly these keys:
{"species_id": <id|null>, "species_confidence": <0-1>, "wood_category_ids": [<id>,...],
"wood_category_confidence": <0-1>, "edge_type_term_id": <id|null>, "edge_confidence": <0-1>,
"figure_term_ids": [<id>,...], "figure_confidence": <0-1>, "feature_tag_ids": [<id>,...],
"feature_tag_confidence": <0-1>}
Runtime appends `LISTS:` (JSON of the five closed lists) plus SKU/length/thickness and the photos.
**`call2` seed.** Shipped text in `backend/inference.py` (`CALL2_SEED`), mirrored here for review.
Write listing copy for this slab for woodworkers choosing stock for a project. Audience:
makers matching wood to a tabletop, bar, bench, or similar build — practical,
material/figure/spec-forward. No lifestyle or marketing hype (avoid empty phrases like
elevate, stunning, dramatic visual contrast).
You write BOTH the short description and the long description.
PLAIN TEXT ONLY (short AND long):
- Output plain text only. No HTML tags (`<p>`, `<ul>`, `<li>`, `<strong>`, `<br>`, etc.).
No markdown bold/headers that look like markup (`**bold**`, `#` headers).
- Structure the long description with newlines and `- ` bullets when helpful.
Rules that always apply:
- Dimensions in the DIMENSION FACT BLOCK are exact: use those strings; invent no numbers.
- Use taxonomy names from DECLARED ATTRIBUTES; do not invent species, edge, figure, grade,
moisture, or features.
- Use the Brand voice and Geography sections when provided. If Brand voice is empty, stay
neutral. Never invent return, warranty, or shipping policies.
- The product name may already appear in DECLARED ATTRIBUTES — do not invent a new title.
- Lead with usable traits (species, figure, size, edge, moisture, grade) and what the piece
suits (tabletop, bar, etc.) without ad-copy fluff.
- Specs live in taxonomy and dimension fields on the product. Do NOT restate DECLARED
ATTRIBUTES or the DIMENSION FACT BLOCK as a labeled metadata list in the description
(no Category:/Figure:/Edge:/Grade:/Moisture:/Length:/Board Feet: dump, no "Full specs"
section, no "Maple Slab" attribute-header block). You may weave 1–2 key traits into
prose or benefit bullets naturally.
SHORT DESCRIPTION (~50 words — most important on-page copy; plain text only):
1. Lead with a practical outcome for a maker (what they can build / how the stock helps),
not lifestyle hype and not a bare species list.
2. Add the key differentiator or most important spec (figure, edge, size, moisture, grade).
3. Close with a trust signal or concrete reason to choose this piece.
LONG DESCRIPTION (plain text only — newlines and `- ` bullets; no HTML, no markdown markup).
Hook + Benefits only (+ brief trust close). Do NOT add a Full specs / Attribute list /
product-name header that re-lists DECLARED ATTRIBUTES or DIMENSION FACT BLOCK:
1. Hook — one sentence on what the piece suits and its standout usable trait.
2. Benefits — 3–5 bullets; each maker-useful trait first, then the supporting detail.
Weave in at most 1–2 key traits naturally — not an exhaustive labeled catalog.
3. Trust close (optional, brief) — return/warranty/brand; use Brand voice when present;
otherwise a short neutral close with no invented policies.
Return ONE JSON object only (no prose outside JSON) with exactly these keys:
`{"short_description": "<plain text ~50 words>", "description": "<plain text long description>"}`
Runtime appends DECLARED ATTRIBUTES (title + taxonomy; not a prior short), DIMENSION FACT
BLOCK, and `Brand voice:` / `Geo:` from Settings (`SETTINGS-30`). The POST handler parses the
JSON, strips any residual HTML to plain text, drops a labeled Category:/Length:/Board Feet:
attribute dump if the model still emits one, and persists both fields.
## 7. Prompt management
- The active Call 1 and Call 2 revisions are editable through the §2 interface; saving creates a
new revision.
- A revision identifies its content, author, and timestamp; `inference_log` records the revision
used for each call.
- The revision interface requires authentication before it is exposed beyond the trusted network
(`METADATA-107`).