Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SUPERDOCS_API_KEY=your_key_here
14 changes: 14 additions & 0 deletions use-cases/priyansh-0304/apparel-techpack-generator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Environment
.env
venv/

# Python
__pycache__/
*.pyc
.pytest_cache/

# Output
output/

# OS
.DS_Store
88 changes: 88 additions & 0 deletions use-cases/priyansh-0304/apparel-techpack-generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Apparel Technical Pack Generator

Generates a factory-ready technical pack (graded measurement spec, bill of
materials, construction notes, flat sketches) for an apparel style, built on
the SuperDocs API.

## What it does

1. Loads a style definition (`styles/*.json`) containing points of measure,
grade rules, materials, and construction notes.
2. **Grades every measurement point deterministically in code** — the AI
never computes sizing math. `techpack/grading.py` applies
`value = base_value + grade_rule × steps_from_base_size` per point, so
correctness across sizes is guaranteed by arithmetic, not by trusting a
model.
3. Builds an initial HTML document with the graded table already baked in,
sends it to SuperDocs for formatting polish and AI-generated flat
sketches, and **re-verifies the measurement table against the source
data after every edit** (`techpack/verification.py`) — the pipeline
never trusts a chat response's own claim that numbers are unchanged.
4. Exports the final result as a `.docx` via SuperDocs' export endpoint.

## Setup

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt
cp .env.example .env # add your SUPERDOCS_API_KEY

## Run

python3 main.py

Output lands in `output/<style_id>_techpack.docx`.

## Tests

python3 -m pytest tests/ -v

17 tests, all mocked — grading correctness, JSON-string-in-JSON response
parsing, and table-verification logic, none requiring a live API key.

## Design decisions & assumptions (logged as I went)

- **Grading is code, not AI.** The task brief notes that where a build
handles figures it can't independently verify, it's graded on whether it
*detects and surfaces* problems, not whether numbers happen to be right.
Rather than ask the model to grade sizes, the math is deterministic and
the AI is only used for formatting and imagery — removing the risk
entirely rather than trying to catch it after the fact.
- **`export` endpoint path was undocumented for the Bearer-key API.**
Found the real payload shape (`POST /v1/documents/export`, full HTML in
the body, raw file bytes back) by inspecting the web app's own network
traffic in DevTools. Confirmed working from `api.superdocs.app` with a
Bearer key in this build.
- **`approve`/`pending_changes` never triggered in testing.** Every edit
sent during development — including a full 6-section rewrite — came back
`auto_approved`. `approve_changes()` is implemented and wired into
`main.py`'s flow but is unverified against a live response, since no
instruction sent during this task ever produced `requires_approval: true`.
Logged as an open gap rather than assumed correct.
- **Sessions are stateful and persist edits.** Reusing a `session_id`
continues editing the same in-progress document; a repeated,
already-satisfied instruction correctly returns a no-op
(`document_changes: null`) rather than reapplying itself.

## Known bugs found while building

- A tested edit's `response` field claimed a formatting-only change while
the returned HTML contained an entirely fabricated, unrelated
"Operational Correspondence" letter with invented reference numbers and
unfilled placeholder fields — reported as success despite being wrong.
- Requesting a single front/back flat sketch pair sometimes inserted two
duplicate pairs into the document instead of one.
- An early response's `response` field returned a generic "wasn't able to
put together a safe reply" fallback message despite the underlying edit
succeeding correctly.

## Project structure

styles/ style definitions (POMs, materials, construction)
techpack/
grading.py deterministic size grading
generator.py builds initial document HTML
superdocs_client.py API wrapper (chat, approve, export)
verification.py post-edit correctness checks
tests/ pytest suite, no live key required
main.py orchestrates the full pipeline
93 changes: 93 additions & 0 deletions use-cases/priyansh-0304/apparel-techpack-generator/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import json
import time
from pathlib import Path

from dotenv import load_dotenv
from techpack.generator import generate_tech_pack_html
from techpack.superdocs_client import send_edit, approve_changes, export_document, SuperDocsError
from techpack.verification import verify_measurement_table

load_dotenv()

STYLE_PATH = Path("styles/classic_crew_tee.json")
OUTPUT_DIR = Path("output")


def load_style(path: Path) -> dict:
with open(path) as f:
return json.load(f)


def get_updated_html(result: dict, fallback_html: str) -> str:
"""Returns the new HTML if the edit produced changes, otherwise falls
back to whatever HTML we already had — a no-op is not an error."""
changes = result.get("document_changes") or {}
updated = changes.get("updated_html")
if updated:
return updated
print("ℹ️ No changes returned (edit was a no-op) — keeping prior HTML state.")
return fallback_html


def main():
style = load_style(STYLE_PATH)
session_id = f"techpack-{style['style_id']}-{int(time.time())}"

html = generate_tech_pack_html(style)
print(f"Generated HTML for {style['style_name']} ({len(html)} chars)")

result = send_edit(
session_id=session_id,
message="Format this document cleanly with a professional letterhead style. Do not change any numbers in the measurement table.",
document_html=html,
)
print("Chat response:", result.get("response"))

latest_html = get_updated_html(result, html)
is_valid, mismatches = verify_measurement_table(latest_html, style)
print("✅ Table verified after formatting" if is_valid else f"❌ Table mismatch after formatting: {mismatches}")

result = send_edit(
session_id=session_id,
message=(
"Generate a front-view and back-view black and white technical flat "
"sketch of a classic crew neck t-shirt and insert them at the top of "
"the document, above the measurement table."
),
)
print("Chat response:", result.get("response"))

latest_html = get_updated_html(result, latest_html)
is_valid, mismatches = verify_measurement_table(latest_html, style)
print("✅ Table still verified after image insertion" if is_valid else f"❌ Table mismatch after images: {mismatches}")

if "<img" not in latest_html:
print("⚠️ No <img> tag found in returned HTML — images may not have actually been inserted despite the success message.")

changes = result.get("document_changes") or {}
version_id = changes.get("version_id")

if changes.get("requires_approval") or changes.get("pending_changes"):
if not version_id:
print("⚠️ Approval required but no version_id was returned — cannot approve. Skipping approval step.")
else:
try:
approve_changes(version_id=version_id, approve_all=True)
print("Changes approved.")
except SuperDocsError as e:
print(f"Approve step failed: {e}")
else:
print("No approval required — changes were auto-approved.")

OUTPUT_DIR.mkdir(exist_ok=True)
try:
file_bytes = export_document(html=latest_html, filename=style["style_id"])
out_path = OUTPUT_DIR / f"{style['style_id']}_techpack.docx"
out_path.write_bytes(file_bytes)
print(f"Exported to {out_path}")
except SuperDocsError as e:
print(f"Export step failed: {e}")


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pytest>=8.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests>=2.31.0
python-dotenv>=1.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"style_id": "TEE-001",
"style_name": "Classic Crew Neck Tee",
"base_size": "M",
"size_range": ["XS", "S", "M", "L", "XL", "XXL"],
"unit": "inches",
"measurement_points": [
{"point_name": "Chest Width", "measured_at": "1 in below armhole, laid flat", "base_value": 20.0, "grade_rule": 1.0, "tolerance": 0.5},
{"point_name": "Body Length", "measured_at": "HPS to hem", "base_value": 28.0, "grade_rule": 0.75, "tolerance": 0.5},
{"point_name": "Shoulder Width", "measured_at": "seam to seam", "base_value": 18.0, "grade_rule": 0.5, "tolerance": 0.25},
{"point_name": "Sleeve Length", "measured_at": "shoulder seam to cuff", "base_value": 8.5, "grade_rule": 0.375, "tolerance": 0.25},
{"point_name": "Armhole Depth", "measured_at": "straight, shoulder to underarm", "base_value": 9.5, "grade_rule": 0.375, "tolerance": 0.25},
{"point_name": "Neck Width", "measured_at": "seam to seam", "base_value": 7.0, "grade_rule": 0.125, "tolerance": 0.125},
{"point_name": "Sleeve Opening", "measured_at": "flat, hemmed edge", "base_value": 7.0, "grade_rule": 0.25, "tolerance": 0.125},
{"point_name": "Hem Width", "measured_at": "flat, bottom edge", "base_value": 20.0, "grade_rule": 1.0, "tolerance": 0.5}
],
"materials": [
{"component": "Main fabric", "spec": "100% combed cotton, single jersey knit, 180 GSM", "placement": "Body panels, sleeves"},
{"component": "Neck ribbing", "spec": "1x1 ribbed cotton, self-fabric, 2cm finished height", "placement": "Neckline"},
{"component": "Main label", "spec": "Woven polyester, 3x4cm", "placement": "Center back neck"},
{"component": "Thread", "spec": "100% polyester, tex 27, color-matched", "placement": "All seams"}
],
"construction_notes": [
"Side-seam construction, not tubular",
"Shoulder seams taped to prevent stretch-out",
"Neckband: 1x1 self-fabric rib, 3-thread overlock attach, 2-needle coverstitch topstitch",
"Hem: double-needle coverstitch, 1cm width",
"Stitch density: 10-12 SPI on main seams"
]
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Builds the tech pack HTML from a style dict. All measurement values here
are already-graded, code-computed numbers — no AI involvement in the math."""

from techpack.grading import build_graded_spec


def generate_tech_pack_html(style: dict) -> str:
graded = build_graded_spec(style)
sizes = style["size_range"]

row_parts = []
for point in style["measurement_points"]:
cells = "".join(f"<td>{graded[point['point_name']][s]}\"</td>" for s in sizes)
row_parts.append(f"<tr><td>{point['point_name']}</td>{cells}<td>±{point['tolerance']}\"</td></tr>")
rows = "".join(row_parts)

bom_rows = "".join(
f"<tr><td>{m['component']}</td><td>{m['spec']}</td><td>{m['placement']}</td></tr>"
for m in style["materials"]
)
construction_list = "".join(f"<li>{note}</li>" for note in style["construction_notes"])

return f"""
<h1>{style['style_name']} — Technical Pack</h1>
<p><b>Style ID:</b> {style['style_id']} | <b>Base Size:</b> {style['base_size']}</p>

<h2>Graded Measurement Specification</h2>
<table border="1">
<tr><th>Point of Measure</th>{"".join(f"<th>{s}</th>" for s in sizes)}<th>Tolerance</th></tr>
{rows}
</table>

<h2>Bill of Materials</h2>
<table border="1">
<tr><th>Component</th><th>Spec</th><th>Placement</th></tr>
{bom_rows}
</table>

<h2>Construction Notes</h2>
<ul>{construction_list}</ul>
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Deterministic size grading — the AI never touches these numbers."""

SIZE_ORDER = ["XS", "S", "M", "L", "XL", "XXL"]


def grade_measurement(base_value: float, grade_rule: float, base_size: str, target_size: str) -> float:
try:
base_idx = SIZE_ORDER.index(base_size)
target_idx = SIZE_ORDER.index(target_size)
except ValueError as e:
raise ValueError(
f"Unsupported size in grading: {e}. Supported sizes are: {SIZE_ORDER}. "
f"Got base_size={base_size!r}, target_size={target_size!r}."
) from e
steps = target_idx - base_idx
return round(base_value + (grade_rule * steps), 3)


def build_graded_spec(style: dict) -> dict:
"""Returns {point_name: {size: value}} for every measurement point in the style."""
base_size = style["base_size"]
table = {}
for point in style["measurement_points"]:
table[point["point_name"]] = {
size: grade_measurement(point["base_value"], point["grade_rule"], base_size, size)
for size in style["size_range"]
}
return table
Loading