Evora on-device text model (Qwen3-1.7B, v2)
Runs the text side of the Evora apps (iPhone and Android) on the phone itself, answering the same requests Evora's server sends its cloud model:
| Task | What it does |
|---|---|
meal_plan.stub |
one meal idea for a slot of the weekly plan: title, description, servings, estimated macros, key ingredients |
meal_plan.details |
the full recipe for an idea: prep and cook time, ingredients with quantities, steps |
coaching.rate_item |
a verdict on a scanned product against the user's goals: score, grade, pros, cons, alternatives |
coaching.summary |
the coach's briefing: up to five insights on nutrition, weight, recovery, workout readiness and trends, plus one push-notification action item — new in v2 |
meal.identify.text |
a described meal as components with an English food-database query and estimated grams |
The model never computes nutrition values from a food database. For a recognised meal the Evora apps send the component queries and weights to the Evora server, which looks them up in USDA FoodData Central and Open Food Facts.
Files
| File | Size | SHA-256 |
|---|---|---|
evora-qwen3-1.7b-v2.Q4_K_M.gguf |
1.11 GB | 8ae00fb3a6be09d023ee5ba0a0025cb1750cab22e44b865027b6d71d2bf872a0 |
Prompt format
The model was trained on exactly one prompt shape; use it verbatim.
- System:
You are Evora's on-device model. Task: <task>. Language: <de|en> (<German|English>). Reply with the JSON object only.Any language other than German is asked in English. - User: the JSON request Evora's server sends its cloud model for that task (see the
samples). For
coaching.summarythe request is written with every key sorted and every number rounded to two decimals;meal.identify.textsendsMeal description: <text>. - Assistant: one compact JSON object in the Evora API's schema.
Qwen3's chat template with thinking off: the assistant turn opens with an empty think block, exactly as in training. Without it the model starts reasoning in prose, and under a JSON grammar that prose ends up inside a string.
<|im_start|>system
You are Evora's on-device model. Task: meal.identify.text. Language: de (German). Reply with the JSON object only.<|im_end|>
<|im_start|>user
Meal description: 2 Eier mit Vollkornbrot<|im_end|>
<|im_start|>assistant
<think>
</think>
Decode greedily (temperature 0; the apps use 0.7 for meal ideas, for variety) under a JSON grammar. The context the apps allocate is 4,096 tokens; a coaching request with 30 days of history is 2,000–3,300 tokens and its answer about 500.
Samples
Held-out test rows (synthetic users, not real people), run through the published Q4_K_M file with the same llama.cpp code the apps use (llama.cpp b11200, greedy, JSON grammar).
Recognising a described meal — meal.identify.text, German
System
You are Evora's on-device model. Task: meal.identify.text. Language: de (German). Reply with the JSON object only.
User
Meal description: 1 kleine Schale Hummus mit etwas Fladenbrot und dazu 1 Tasse Mokka
Assistant
{
"title": "Hummus mit Fladenbrot und Mokka",
"components": [
{
"name": "Hummus",
"query": "hummus",
"estimated_weight_grams": 180,
"confidence": 0.95
},
{
"name": "Fladenbrot",
"query": "fladenbrot",
"estimated_weight_grams": 80,
"confidence": 0.9
},
{
"name": "Mokka",
"query": "mocha",
"estimated_weight_grams": 250,
"confidence": 0.94
}
]
}
A meal idea for the plan — meal_plan.stub, English
System
You are Evora's on-device model. Task: meal_plan.stub. Language: en (English). Reply with the JSON object only.
User
The request (1,441 characters: the user's goals, today and 30 days of history){
"slot": "dinner",
"language": "en",
"exclude_titles": [
"Chicken Rice Bowl",
"Mediterranean Chicken Rice Bowl",
"Thai Basil Beef Rice Bowl",
"Thai Chicken Rice Bowl",
"Lentil Chicken Curry Bowl"
],
"prefer_ingredients": [
"onion",
"cumin",
"greek yogurt",
"lentils",
"lentil",
"tomato",
"cherry tomatoes",
"milk",
"rice",
"black pepper",
"broccoli",
"brown rice",
"banana",
"peanut butter",
"parsley",
"rolled oats",
"yogurt",
"chicken breast",
"olive oil"
],
"snapshot": {
"request": {
"now": "2026-10-24T12:23:00-04:00"
},
"client": {
"locale": "en_US",
"timezone": "America/New_York"
},
"profile": {
"display_name": "{{name}}",
"sex": "female",
"height_cm": 170,
"weight_kg": 92.0,
"activity_level": "moderate",
"goal": "Lose fat"
},
"targets": {
"kcal": 2385,
"protein_g": 166,
"carbs_g": 268,
"fat_g": 80,
"fiber_g": 30,
"water_ml": 2500
},
"preferences": {
"diet_style": "omnivore",
"allergies": [],
"intolerances": [],
"dislikes": [],
"cuisines": [
"middle eastern",
"indian",
"mexican"
]
},
"pantry": [
"peanut butter",
"Haferflocken",
"sugar",
"Linsen",
"onion"
],
"today_intake": {
"kcal": 855,
"protein_g": 60,
"carbs_g": 100,
"fat_g": 30
},
"body_trends": {
"weight_7d_avg_kg": 92.0,
"weight_30d_avg_kg": 92.3
},
"training": {
"last_7_days": [
{
"date": "2026-10-18",
"type": "run",
"duration_min": 75
},
{
"date": "2026-10-19",
"type": "swim",
"duration_min": 50
},
{
"date": "2026-10-20",
"type": "run"
},
{
"date": "2026-10-21",
"type": "rest"
},
{
"date": "2026-10-22",
"type": "rest"
},
{
"date": "2026-10-23",
"type": "cardio",
"duration_min": 40
},
{
"date": "2026-10-24",
"type": "rest"
}
]
}
}
}
Assistant
{
"title": "Lentil Chicken Rice Bowl",
"description": "Grilled chicken breast with brown rice, lentils, onion and tomato, finished with parsley and black pepper.",
"servings": 1,
"estimated_kcal": 620,
"estimated_protein_g": 42,
"estimated_carbs_g": 68,
"estimated_fat_g": 18,
"key_ingredients": [
"chicken breast",
"brown rice",
"lentils",
"onion",
"tomato",
"parsley",
"black pepper"
]
}
The recipe for an idea — meal_plan.details, German
System
You are Evora's on-device model. Task: meal_plan.details. Language: de (German). Reply with the JSON object only.
User
{
"language": "de",
"slot": "lunch",
"title": "Hähnchen-Bulgur-Bowl mit Kräutern",
"description": "Gegrilltes Hähnchen auf Bulgur mit Kichererbsen, Gurke, Tomate und Kräutern, dazu ein leichtes Zitronen-Tahin-Dressing.",
"servings": 1,
"estimated_kcal": 620,
"estimated_protein_g": 58,
"estimated_carbs_g": 55,
"estimated_fat_g": 18
}
Assistant
{
"prep_minutes": 10,
"cook_minutes": 20,
"ingredients": [
{
"name": "Bulgur",
"quantity": 70,
"unit": "g",
"note": "gewaschen"
},
{
"name": "Hähnchenbrustfilet",
"quantity": 180,
"unit": "g",
"note": "in Streifen geschnitten"
},
{
"name": "Kichererbsen",
"quantity": 80,
"unit": "g",
"note": "abgetropft"
},
{
"name": "Gurke",
"quantity": 120,
"unit": "g",
"note": "gewürfelt"
},
{
"name": "Tomate",
"quantity": 100,
"unit": "g",
"note": "in Scheiben"
},
{
"name": "Zitronensaft",
"quantity": 1,
"unit": "tbsp",
"note": "frisch gepresst"
},
{
"name": "Tahin",
"quantity": 1,
"unit": "tbsp",
"note": "frisch"
},
{
"name": "Olivenöl",
"quantity": 1,
"unit": "tsp",
"note": null
},
{
"name": "Knoblauch",
"quantity": 1,
"unit": "clove",
"note": "fein gehackt"
},
{
"name": "Paprikapulver",
"quantity": 0.5,
"unit": "tsp",
"note": "edelsüß"
},
{
"name": "Salz",
"quantity": 0.5,
"unit": "tsp",
"note": null
},
{
"name": "Schwarzer Pfeffer",
"quantity": 0.25,
"unit": "tsp",
"note": "frisch gemahlen"
}
],
"steps": [
"Koche die Bulgur nach Packungsangabe in leicht gesalzenem Wasser gar und rühre sie bei Bedarf ab.",
"Erhitze das Olivenöl in einer Pfanne und brate das Hähnchen mit Paprikapulver, Salz und Pfeffer 4–5 Minuten kräftig an.",
"Gib Kichererbsen, Gurke und Tomate dazu und rühre alles 2 Minuten cremig um.",
"Richte die Bulgur mit dem Hähnchen, Kichererbsen, Gurke und Tomate in einer Schüssel an.",
"Zupfte Zitronensaft mit Tahin und Knoblauch in einer kleinen Schüssel zu einem leichten Dressing und serviere es direkt neben der Bowl."
]
}
Rating a scanned product — coaching.rate_item, English
System
You are Evora's on-device model. Task: coaching.rate_item. Language: en (English). Reply with the JSON object only.
User
{
"item": {
"nutrition": {
"energy_kcal": 67.19999999999993,
"fat_g": 0.27,
"saturated_fat_g": 0.054,
"carbs_g": 10.799999999999999,
"sugars_g": 0.44999999999999996,
"fiber_g": 2.88,
"protein_g": 1.494,
"salt_g": 0.23399999999999999,
"sodium_mg": 93.6
},
"estimated_weight_grams": 18,
"barcode": "20171841",
"serving_size_g": 18,
"ingredients": [
"Roggenvollkornmehl",
"Speisesalz"
],
"title": "Tastino — Roggen Vollkorn-Knäckebr",
"brand": "Tastino",
"nutrition_per_100g": {
"energy_kcal": 373.333333333333,
"fat_g": 1.5,
"saturated_fat_g": 0.3,
"carbs_g": 60,
"sugars_g": 2.5,
"fiber_g": 16,
"protein_g": 8.3,
"salt_g": 1.3,
"sodium_mg": 520
}
},
"profile": {
"goal": "Lose fat"
},
"targets": {
"energy_kcal": 1938,
"protein_g": 189
},
"goals": {
"all": [
"Maintain",
"Lose fat",
"Eat healthier"
],
"primary": "Lose fat",
"diet": [
"No restrictions"
]
}
}
Assistant
{
"generated_at": "2026-09-26T12:33:44Z",
"language": "en",
"score": 62,
"grade": "C",
"verdict": "eat_in_moderation",
"headline": "Decent low-calorie snack, but not a strong fat-loss fit",
"rationale": "One 18 g serving gives 67.2 kcal, about 3.47% of your 1938 kcal target, with just 1.5 g of protein and 2.88 g of fiber. That makes it easy to overeat for fat loss, so keep portions tight and avoid using it as a regular snack.",
"pros": [
"Low calorie per serving",
"Low sugar",
"Moderate fiber for size"
],
"cons": [
"Very low protein",
"Easy to overeat",
"Mostly refined carbs"
],
"better_alternatives": [
"Greek yogurt with berries",
"High-protein cereal",
"Edamame or canned beans"
],
"action_item": "Use it as a small snack, not a meal, and limit it to 1 serving at most.",
"tone": "neutral"
}
The coach's briefing — coaching.summary, German
System
You are Evora's on-device model. Task: coaching.summary. Language: de (German). Reply with the JSON object only.
User
The request (4,280 characters: the user's goals, today and 30 days of history){
"body": {
"days_since_last_weight": 0,
"weight_history_weekly": [
{
"date": "2026-09-27",
"kg": 85.2
}
],
"weight_kg_latest": 85.2
},
"client": {
"app": "evora-ios",
"locale": "de_AT",
"platform": "iOS",
"tz": "Europe/Vienna",
"version": "1.0"
},
"history": {
"consistency_last_30": 2,
"current_logging_streak_days": 0,
"daily_last_30": [
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-01",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-02",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-03",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-04",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-05",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-06",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-07",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-08",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-09",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-10",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-11",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-12",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-13",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-14",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-15",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-16",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 1501.55,
"carbs_g": 195.4,
"day": "2026-09-17",
"fat_g": 55.82,
"meal_count": 3,
"protein_g": 121.25
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-18",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-19",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-20",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-21",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-22",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-23",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-24",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-25",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 2632.95,
"carbs_g": 349.08,
"day": "2026-09-26",
"fat_g": 106.9,
"meal_count": 2,
"protein_g": 190.05
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-27",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-28",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-29",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-09-30",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
}
]
},
"profile": {
"diet": [
"Low carb",
"Pescatarian"
],
"display_name": "{{name}}",
"goals": [
"Eat healthier"
],
"primary_goal": "Eat healthier",
"sex": "male",
"target_weight_kg": 79,
"weight_kg": 85
},
"recovery": {
"averages_7d": {},
"last_night": {
"sleep_bedtime": "2026-09-29T20:00:00Z",
"sleep_hours": 9,
"sleep_quality": 3,
"sleep_source": "user",
"sleep_wake_time": "2026-09-30T05:00:00Z"
}
},
"request": {
"now": "2026-09-30T14:00:00Z",
"slot": "noon"
},
"response_contract": {
"max_insights": 5,
"rules": [
"Use only the supplied facts. Never invent numbers, dates, or events.",
"Quote provided numbers exactly. Do not round or recompute.",
"No medical advice or prescriptive training plans.",
"Return one short action_item (≤ 140 chars) suitable for a push notification."
],
"sections": [
"nutrition",
"weight",
"recovery",
"workoutReadiness",
"trends"
],
"tone_values": [
"positive",
"neutral",
"caution"
]
},
"schema_version": "1.0",
"targets": {
"calories_kcal": 2100,
"protein_g": 153,
"water_ml": 2500
},
"today": {
"healthkit": {
"external_water_ml": 500,
"steps": 13040
},
"meals": [],
"remaining_to_target": {
"calories_kcal": 2100,
"protein_g": 153,
"water_ml": 1250
},
"totals": {
"calories_kcal": 0,
"carbs_g": 0,
"fat_g": 0,
"fiber_g": 0,
"protein_g": 0,
"salt_g": 0,
"saturated_fat_g": 0,
"sodium_mg": 0,
"sugars_g": 0,
"water_ml": 1250
}
}
}
Assistant
{
"generated_at": "2026-09-30T14:00:00Z",
"insights": [
{
"section": "nutrition",
"title": "Mittag ist dein beste Chance",
"message": "Du bist noch 2100 kcal und 153 g Protein fehlend, also noch 1250 ml Wasser und 153 g Protein zu erreichen. Für heute schließt das Abendessen mit einer klaren Mahlzeit ab und steuert die Kalorien und Protein direkt nach unten.",
"tone": "positive"
},
{
"section": "recovery",
"title": "Schlaf ist heute gut",
"message": "Du schläft 9 Stunden, Qualität 3/5, von 20:00 bis 05:00. Das ist für gesunde Ernährung und Appetit sehr gut, und die aktuelle Schlafqualität ist jetzt der beste Tag.",
"tone": "neutral"
},
{
"section": "trends",
"title": "Logging ist zu ungenau",
"message": "Dein Logging-Intervall liegt bei 0 Tagen, 30-Tage-Consistency bei 2, und die aktuelle Logging-Serie bei 0 Tagen. Das macht es schwierig, eine gesündere Ernährung zu steuern; logge jetzt die nächsten Mahlzeiten vollständig.",
"tone": "caution"
},
{
"section": "nutrition",
"title": "Low carb ist dein Richtung",
"message": "Du hast 0 g Kohlenhydrate, 0 g Fett und 0 g Protein heute, und die 7-Tage-Schnitt ist leer. Das passt zu Low Carb, aber du musst jetzt die restlichen 153 g Protein und 2100 kcal mit einer Mahlzeit abdecken.",
"tone": "neutral"
}
],
"action_item": "Schließe heute Abend eine Mahlzeit mit 153 g Protein und 2100 kcal, damit du die Ernährung klar steuern kannst."
}
The coach's briefing — coaching.summary, English
System
You are Evora's on-device model. Task: coaching.summary. Language: en (English). Reply with the JSON object only.
User
The request (4,321 characters: the user's goals, today and 30 days of history){
"body": {
"days_since_last_weight": 0,
"weight_history_weekly": [
{
"date": "2026-11-27",
"kg": 95.5
},
{
"date": "2026-12-03",
"kg": 95.7
}
],
"weight_kg_latest": 95.7
},
"client": {
"app": "evora-ios",
"locale": "en_GB",
"platform": "iOS",
"tz": "Europe/London",
"version": "1.0"
},
"history": {
"consistency_last_30": 2,
"current_logging_streak_days": 0,
"daily_last_30": [
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-05",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-06",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-07",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-08",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 3258.61,
"carbs_g": 323.08,
"day": "2026-11-09",
"fat_g": 143.74,
"meal_count": 1,
"protein_g": 173.75
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-10",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-11",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-12",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-13",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-14",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-15",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-16",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-17",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-18",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-19",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-20",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-21",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-22",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-23",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-24",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-25",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-26",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-27",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-28",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-29",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-11-30",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 3156.39,
"carbs_g": 333.55,
"day": "2026-12-01",
"fat_g": 122.33,
"meal_count": 5,
"protein_g": 257.32
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-12-02",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-12-03",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
},
{
"calories_kcal": 0,
"carbs_g": 0,
"day": "2026-12-04",
"fat_g": 0,
"meal_count": 0,
"protein_g": 0
}
]
},
"profile": {
"diet": [
"No restrictions"
],
"display_name": "{{name}}",
"goals": [
"Better sleep",
"Improve energy",
"Eat healthier"
],
"primary_goal": "Improve energy",
"sex": "male",
"target_weight_kg": 85.6,
"weight_kg": 95.6
},
"recovery": {
"averages_7d": {
"resting_hr_bpm": 54.98,
"sleep_hours": 8.28
},
"last_night": {
"resting_hr_bpm": 57,
"sleep_hours": 4.74,
"sleep_source": "healthkit"
}
},
"request": {
"now": "2026-12-04T07:30:00Z",
"slot": "evening"
},
"response_contract": {
"max_insights": 5,
"rules": [
"Use only the supplied facts. Never invent numbers, dates, or events.",
"Quote provided numbers exactly. Do not round or recompute.",
"No medical advice or prescriptive training plans.",
"Return one short action_item (≤ 140 chars) suitable for a push notification."
],
"sections": [
"nutrition",
"weight",
"recovery",
"workoutReadiness",
"trends"
],
"tone_values": [
"positive",
"neutral",
"caution"
]
},
"schema_version": "1.0",
"targets": {
"calories_kcal": 2550,
"protein_g": 172,
"water_ml": 2000
},
"today": {
"healthkit": {
"active_energy_kcal": 327.75,
"steps": 9846
},
"meals": [],
"remaining_to_target": {
"calories_kcal": 2550,
"protein_g": 172,
"water_ml": 750
},
"totals": {
"calories_kcal": 0,
"carbs_g": 0,
"fat_g": 0,
"fiber_g": 0,
"protein_g": 0,
"salt_g": 0,
"saturated_fat_g": 0,
"sodium_mg": 0,
"sugars_g": 0,
"water_ml": 1250
}
}
}
Assistant
{
"generated_at": "2026-12-04T07:30:00Z",
"insights": [
{
"section": "nutrition",
"title": "Eat a full meal now",
"message": "You are 0 of 2550 kcal and 0 of 172 g protein short. Close that gap now with a full meal and add protein to make energy more stable.",
"tone": "caution"
},
{
"section": "recovery",
"title": "Sleep is too short",
"message": "Last night was 4.74 h, down from 8.28 h in 7 days. That pulls energy and sleep goals; get a full night tonight.",
"tone": "caution"
},
{
"section": "weight",
"title": "Weight is stable",
"message": "Your latest weight is 95.7 kg, up from 95.5 kg on 2026-11-27. That supports your goal without moving toward a new target.",
"tone": "neutral"
},
{
"section": "trends",
"title": "Logging is inconsistent",
"message": "Your 30-day consistency is 2, and your current logging streak is 0 days. That makes it hard to build a reliable energy pattern; log every meal tonight.",
"tone": "caution"
}
],
"action_item": "Eat a full meal now and add at least 30 g of protein to close the energy gap."
}
Running it
With llama.cpp's server (OpenAI-compatible API):
llama-server -m evora-qwen3-1.7b-v2.Q4_K_M.gguf --jinja --temp 0 -c 4096
Send the system and user messages as above with "chat_template_kwargs": {"enable_thinking": false}
and "response_format": {"type": "json_object"}.
Evaluation
36 held-out requests (6 per task and 12 coaching briefings, half German, half English; synthetic users), run through the published Q4_K_M file with llama.cpp b11200, greedy, JSON grammar:
| Task | Cases | Valid JSON | Valid schema | Notes |
|---|---|---|---|---|
meal_plan.stub |
6 | 100% | 100% | |
meal_plan.details |
6 | 100% | 100% | |
coaching.rate_item |
6 | 100% | 100% | |
meal.identify.text |
6 | 100% | 100% | |
coaching.summary |
12 | 100% | 100% | right language 100%; 97% of the numbers quoted appear in the request (gpt-5.4, the teacher: 91%); 4.25 insights on average |
Held-out loss of the training run: 0.710.
Training
- Base: Qwen/Qwen3-1.7B, LoRA (r 32, alpha 64, dropout 0.05; 12.8M trainable parameters) on the attention projections (q, k, v, o), trained on the assistant turns only, merged, converted and quantized with llama.cpp.
- v1: 4,654 training rows for the four original tasks. Inputs are shaped exactly like the apps' requests (synthetic users; product ratings on Open Food Facts products, ODbL); the answers were written by gpt-5.4 answering Evora's production prompts. Stopped at step 305 of 582 once the held-out loss had flattened (0.535).
- v2: continued from v1 on 2,548 rows: 1,648
coaching.summaryconversations (German and English; synthetic users and days in the apps' exact request schema, answered by gpt-5.4 with Evora's production coaching prompt) and 900 rows replayed from v1 so the original tasks are not forgotten. Sequence length 4,096, learning rate 1e-4. This revision (27 September 2026) replaces the first v2 upload, whose training data had 179 German coaching requests answered in English by the teacher; they were asked again in German. Stopped at step 60 of 90 at a held-out loss of 0.710 (the first upload: 0.748). - Trained in AI Studio: v1 and v2's first 36 steps on an AMD RX 6900 XT, the rest of v2 on an Apple M3.
Limitations
- A 1.7-billion-parameter model at 4 bits. Its German is clearly weaker than its English: recipes are mostly fine, but coaching text has grammar slips and now and then a muddled sentence, as in the German briefing above. The numbers it quotes almost always come from the request, but it occasionally repeats one where another was meant.
- Estimated grams and macros are estimates; nutrition values for logged meals come from the food databases, not from the model.
- The coach gives everyday advice (meal composition, portions, sleep, walks). It is not a medical device, and not for clinical nutrition or training decisions.
Licence
Apache-2.0, as the base model. Product data in the rating task's training inputs: Open Food Facts, ODbL.