An AI-powered web application for creating professional essential oil blends following proper perfumery principles. Uses @tetherto/llm-llamacpp for intelligent blend recommendations based on top, middle, and base notes.
- ๐ค AI-Powered Blending: Uses advanced LLM for intelligent recommendations
- ๐งช Professional Perfumery: Follows top/middle/base note structure
- ๐ธ Rich Oil Database: 20+ essential oils with detailed profiles
- ๐จ Beautiful Web UI: Modern, responsive interface
- โก Smart Fallback: Intelligent demo mode when LLM is busy
- ๐ Performance Metrics: Real-time inference statistics
- ๐ Dual Backend: LLM primary + rule-based fallback
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Web Browser โโโโโโ Express Server โโโโโโ Bare + LLM โ
โ (React-like UI)โ โ (Node.js) โ โ (@tetherto) โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโ
โ Essential Oils โ
โ Database โ
โโโโโโโโโโโโโโโโโโโ
- Node.js >= 18.x
- Bare Runtime >= 1.17.3
- GitHub Personal Access Token with
read:packagesscope
npm install -g bare-runtimeVerify installation:
bare -v # Should be >= 1.17.3Create a .npmrc file in the project root:
@tetherto:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN_HERE- Clone/Download the project
- Install dependencies:
npm install
- Start the server:
node server.js
- Open browser to: http://localhost:3000
- Select oils by clicking the cards (they highlight green)
- Enter description (e.g., "relaxing evening blend")
- Click "Create My Blend" to get AI recommendations
- View results with exact percentages and rationales
POST /api/blend
{
"oils": ["lavender", "bergamot", "sandalwood"],
"description": "relaxing evening blend"
}Response:
{
"success": true,
"recommendation": "BLEND RECOMMENDATION:\n- lavender: 50% - MIDDLE note...",
"availableOils": ["lavender", "bergamot", "sandalwood"],
"targetDescription": "relaxing evening blend",
"stats": {"TTFT": 23805.949, "TPS": 5.912},
"fallback": false
}# Direct LLM usage
bare scentBlenderBare.js "lavender,bergamot" "calming blend"
# Server with web UI
node server.jsOur database includes 20+ oils with detailed profiles:
Citrus (TOP notes): Bergamot, Lemon, Orange, Grapefruit Floral (MIDDLE notes): Lavender, Rose, Jasmine, Ylang-ylang Herbal (TOP/MIDDLE): Peppermint, Eucalyptus, Clary Sage Woody (BASE notes): Sandalwood, Cedarwood Earthy (BASE notes): Vetiver, Patchouli Resinous (MIDDLE notes): Frankincense
Each oil includes:
- Scent notes array
- Intensity level (light/medium/strong/heavy)
- Category classification
- Human-readable description
The system follows traditional perfumery structure:
- TOP NOTES (20%): First impression, evaporate in 15-120 minutes
- MIDDLE NOTES (50%): Heart of blend, last 2-4 hours
- BASE NOTES (30%): Foundation, lasting 6+ hours
- Input Validation: Filters oils against database
- LLM Processing: Sends structured prompt to @tetherto/llm-llamacpp
- Smart Parsing: Extracts blend data from streaming response
- Fallback Logic: Uses rule-based system if LLM fails
When LLM is unavailable:
- Categorizes oils by perfumery notes
- Selects exactly 1 TOP, 1 MIDDLE, 1 BASE
- Applies professional ratios
- Generates descriptive rationales
scents/
โโโ server.js # Express web server + API
โโโ scentBlenderBare.js # Bare runtime LLM script
โโโ essentialOils.js # Oil database
โโโ public/
โ โโโ index.html # Web UI (single file app)
โโโ package.json # Dependencies
โโโ .npmrc # GitHub registry config
โโโ README.md # This file
- GET
/- Web UI - POST
/api/blend- Generate blend recommendation - GET
/api/oils- Get essential oils database - GET
/api/health- Server health check
Edit server.js constants:
const PORT = 3000; // Server port
const TIMEOUT = 600000; // LLM timeout (10 mins)Edit scentBlenderBare.js config:
const config = {
gpu_layers: '0', // GPU acceleration
ctx_size: '1024', // Context window
device: 'cpu', // cpu/gpu
temp: '0.7', // Creativity (0.1-2.0)
top_p: '0.9', // Nucleus sampling
top_k: '40', // Top-k sampling
predict: '300' // Max output tokens
}"Module not found" error:
# Reinstall dependencies
rm -rf node_modules package-lock.json
npm installLLM timeout/fails:
- First run downloads ~4GB model (takes time)
- Increase timeout in server.js
- Check Bare runtime version:
bare -v
GitHub token issues:
- Verify token has
read:packagesscope - Check
.npmrcformat - Try:
npm login --scope=@tetherto --registry=https://npm.pkg.github.com
Port 3000 in use:
# Kill process using port 3000
lsof -ti:3000 | xargs kill -9
# Or change PORT in server.jsAdd debug logging:
// In server.js
console.log('Debug:', { oils, description, result });Edit essentialOils.js to add oils:
"my-oil": {
notes: ["fresh", "green", "herbal"],
intensity: "medium",
category: "herbal",
description: "Custom oil description"
}The system can be extended to use different LLM models by modifying the Bare script's model loading configuration.
Typical Response Times:
- Demo fallback: < 100ms
- LLM (first run): 30-60 seconds (model download)
- LLM (subsequent): 5-15 seconds
- Model size: ~2.4GB (Medgemma-4B-Q4_1)
- Fork the repository
- Create feature branch:
git checkout -b feature-name - Make changes with proper comments
- Test thoroughly
- Submit pull request
This project is open source. The LLM model (@tetherto/llm-llamacpp) has its own license terms.
Built with โค๏ธ and QVAC โข Happy Blending! ๐ฟโจ