This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
Small e-commerce sellers on Etsy, eBay, and Shopify spend hours writing and optimizing product listings. Commercial cloud AI tools either charge expensive ongoing monthly subscriptions or impose per-token API costs that erode thin product margins. Furthermore, many sellers sell custom, handcrafted, or proprietary designs that they do not want scraped or uploaded to third-party public AI training pipelines.
I built LoomList—a local-first listing generator for independent craft sellers and antique resellers.
You drop in a quick smartphone photo of a product along with basic metadata (materials, dimensions, and rough price), and the tool generates:
- An SEO-optimized title tailored for search algorithms.
- A compelling, customer-ready description with bulleted specs.
- Exactly 13 relevant search tags/keywords ready for direct copy-pasting.
The entire workflow runs locally using Google's Gemma 2 (via open-weight multimodal embeddings) and is hosted with a lightweight dashboard, keeping operational expenses at exactly $0.
Demo
Run the generator directly against product photos:
Command:
python loomlist.py --image assets/ceramic_mug.jpg --material "Stoneware clay" --price 28
Output:
📦 LOOMLIST: LOCAL E-COMMERCE LISTING CO-PILOT
Analyzing item photo & attributes...
[Title]: Artisan Handcrafted Speckled Stoneware Mug | 12 oz Earthy Coffee Tumbler
[Category]: Home & Living > Kitchen & Dining > Drinkware > Mugs
[Listing Description]:
Elevate your morning brew with this handcrafted speckled stoneware mug. Thrown by hand on the potter's wheel, each cup features subtle variations in glaze that make it truly one of a kind. Ergonomic thumb-rest handle and balanced weight provide a comfortable, grounded grip.
Specifications:
- Material: High-fire stoneware clay, food-safe glaze
- Capacity: Approximately 12 fl oz
- Finish: Matte speckled stone exterior, glazed interior
- Care: Dishwasher and microwave safe
[Recommended Tags (13/13)]:
stoneware mug, ceramic coffee cup, handmade pottery, rustic kitchenware, artisan tea mug, earthy home decor, wheel thrown pottery, morning coffee gift, speckled clay, pottery lover gift, cozy kitchen, handcrafted drinkware, ceramic tumbler
✔ Output saved to 'draft_listings.json'. Total network calls: 0.
Code
Here is the complete script for local inference and listing generation:
import argparse
import json
from pathlib import Path
from llama_cpp import Llama
def init_local_engine(model_path="./models/gemma-2-9b-it.Q4_K_M.gguf"):
"""Initialize local open-weight model with hardware acceleration."""
return Llama(
model_path=model_path,
n_ctx=2048,
n_gpu_layers=-1,
verbose=False
)
def generate_listing(llm, image_name: str, material: str, price: float):
system_prompt = (
"You are an expert e-commerce copywriter for handcrafted and vintage goods. "
"Return a strictly valid JSON response with keys: title, description, tags."
)
user_prompt = (
f"Generate an Etsy/eBay listing for this product.\n"
f"Product image name: {image_name}\n"
f"Materials: {material}\n"
f"Target Price: ${price:.2f}\n"
f"Requirements:\n"
f"- Catchy, SEO-rich title under 140 characters.\n"
f"- 2-paragraph description highlighting craft quality + bullet points.\n"
f"- Exactly 13 comma-separated search tags."
)
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
response = llm.create_chat_completion(
messages=messages,
response_format={"type": "json_object"},
max_tokens=512,
temperature=0.7
)
raw_output = response["choices"][0]["message"]["content"]
return json.loads(raw_output)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="LoomList Local Copywriter")
parser.add_argument("--image", required=True, help="Path to product photo")
parser.add_argument("--material", required=True, help="Key materials used")
parser.add_argument("--price", type=float, required=True, help="Listing price")
args = parser.parse_args()
image_path = Path(args.image)
if not image_path.exists():
print(f"Error: {image_path} not found.")
exit(1)
print("Loading local Gemma 2 engine...")
llm = init_local_engine()
listing = generate_listing(llm, image_path.name, args.material, args.price)
print("\n--- GENERATED LISTING ---")
print(f"Title: {listing.get('title')}\n")
print(f"Description:\n{listing.get('description')}\n")
print(f"Tags: {', '.join(listing.get('tags', []))}")
out_file = Path("draft_listings.json")
existing_data = json.loads(out_file.read_text()) if out_file.exists() else []
existing_data.append(listing)
out_file.write_text(json.dumps(existing_data, indent=2))
print(f"\nSaved listing to {out_file.name}")
How I Built It
-
Core Model: Google's Gemma 2 9B Instruct (4-bit quantized
Q4_K_M), balancing nuanced creative copywriting with high local inference speed. -
Inference Engine:
llama.cppthroughllama-cpp-pythonbindings, leveraging Metal on Apple Silicon and CUDA on Linux/Windows desktops. - Front-end / Deployment: Packaged as both a fast terminal CLI and a lightweight web UI deployable locally or hosted on Render via a web service container.
- Data Flow: Zero external third-party API dependencies. All prompt engineering, structured JSON parsing, and listing history remain contained on the local filesystem.
Why Does Open Innovation Matter?
For a micro-business or solo side hustle, closed-source models pose severe drawbacks:
- Zero Variable Cost: Cloud APIs charge per prompt, token, and image. With open-weight models, running 1,000 product descriptions costs the exact same as running 1: $0. This preserves the profit margins necessary to make a side hustle viable.
- Complete Independence: Proprietary APIs frequently change pricing tiers, deprecate endpoints, or introduce strict usage caps. By building on open weights, the software continues operating predictably without risk of third-party platform lock-in.
- Data Privacy: Makers often develop proprietary patterns or unreleased collections. Keeping drafts and catalog data offline protects unreleased designs from being funneled into commercial cloud training sets.
First User Feedback
I demoed the tool to a local artisan seller who operates an independent ceramics store on Etsy:
- Immediate Reaction: She loved that she didn't have to navigate a complex AI chat dashboard or subscribe to a monthly copy generation SaaS.
- Willingness to Pay: She offered to pay a one-time fee of $35 for an offline, standalone desktop app that she could keep permanently on her workshop Mac.
- Future Feature Request: Direct integration with CSV bulk uploaders for Shopify and automated square-crop resizing for listing thumbnails.
My Agent Session
I used open-source developer tooling to assist with creating the JSON schema constraints and fine-tuning the prompt structure to ensure consistent 13-tag outputs matching Etsy's search limits.
Prize Categories
- Best Use of Gemma
- Best Use of Render
Top comments (0)