initial commit

This commit is contained in:
2025-08-11 22:24:24 -05:00
commit e10c181a89
23 changed files with 5300 additions and 0 deletions

122
server/index.ts Normal file
View File

@@ -0,0 +1,122 @@
// Very small mock server for local testing. In production replace with your AI provider call.
import express from "express";
import bodyParser from "body-parser";
import cors from "cors";
import { Ollama } from "ollama";
import { XMLParser } from "fast-xml-parser";
const app = express();
app.use(cors());
app.use(bodyParser.json());
const ollama = new Ollama({
host: "http://192.168.1.85:11434",
});
type ChatMessage = {
content: string;
role: "user" | "assistant";
};
const xmlParser = new XMLParser();
const systemPrompt: string = `You are an educated chef that likes to make nutritional meals.
You have an oven, stove top, pots, pans, microwave, grill, nice knives and some other kitchen tools at your disposal.
You do all of your grocery shopping at Trader Joe's, but on rare occasions can go to a different store for a few ingredients.
You like to be able to cook from start to finish in 45 minutes, but up to 60 is acceptable.
You largely prefer to use chicken as your protein but beef and turkey are acceptable on occasion.
You are a strong believer that the microwave should not be used to cook vegetables.
When returning meals, you must put each meal into an XML structure such as
<meal><name></name><ingredients><name></name><quantity></quantity></ingredients><steps></steps></meal>
The steps should be detailed enough for a beginner chef to follow along as if they have never cooked this meal before.
Each meal needs to be able to feed all people involved rather than each person getting a meal. If you are asked for 3 meals you should only return 3 meals.
When combining all ingredients into a shopping list, you must again use an XML
Your entire response needs to be XML <xml><meals></meals><shoppingList></shoppingList></xml>, nothing more, nothing less. If you consider returning something other than XML, you need to stop and start over.
`;
app.post("/api/mealplan", async (req, res) => {
const prompt: string = req.body.prompt || "";
const existingMessages: ChatMessage[] = req.body.existingMessages || [];
let fullPrompt = prompt;
if (!prompt) {
return res.sendStatus(400);
}
if (existingMessages.length === 0) {
fullPrompt = systemPrompt + fullPrompt;
}
const callOllama = () => {
return ollama.chat({
model: "llama3.2:3b",
think: false, //"low",
stream: false,
messages: [
{
content: fullPrompt,
role: "user",
},
],
});
};
const ollamaResponse = await callOllama();
let mealsAndList: any = null;
try {
mealsAndList = xmlParser.parse(ollamaResponse.message.content ?? "");
} catch (e) {
console.log("Failed on first call", e);
}
if (!mealsAndList) {
const ollamaResponse2 = await callOllama();
try {
mealsAndList = xmlParser.parse(ollamaResponse2.message.content ?? "");
} catch (e) {
console.log("Failed on second call", e);
return res.sendStatus(500);
}
}
if (mealsAndList.xml) {
mealsAndList = mealsAndList.xml;
}
let mealPlan: any[] = [];
if (
mealsAndList.meals &&
Array.isArray(mealsAndList.meals) &&
mealsAndList.meals.length > 0
) {
mealPlan = mealsAndList.meals;
} else if (
mealsAndList.meals &&
mealsAndList.meals.meal &&
Array.isArray(mealsAndList.meals.meal) &&
mealsAndList.meals.meal.length > 0
) {
console.log(mealsAndList.meals.meal);
mealPlan = mealsAndList.meals.meal;
} else if (mealsAndList.meals) {
mealPlan = mealsAndList.meals;
}
let shoppingList: any[] = [];
if (mealsAndList.shoppingList) {
shoppingList = mealsAndList.shoppingList;
}
// simple deterministic mock response: you should replace this with a real AI call.
const mock = {
mealPlan,
groceryList: shoppingList,
};
// simulate some delay
await new Promise((r) => setTimeout(r, 600));
res.json(mock);
});
const port = 3000;
app.listen(port, () =>
console.log(`Server listening on http://localhost:${port}`),
);