Explorar o código

[#1] chore: update default models, rewrite allergen check to use cached LLM, and update README grading layout

Lange François hai 3 semanas
pai
achega
58c3849eb5
Modificáronse 53 ficheiros con 374 adicións e 114 borrados
  1. BIN=BIN
      Project.pdf
  2. 2 1
      README.md
  3. BIN=BIN
      Retro Planning.pdf
  4. 70 68
      app.py
  5. 1 1
      configure_zabbix_alerts.py
  6. 1 1
      docs/Backup_Procedure.md
  7. BIN=BIN
      docs/Backup_Procedure.pdf
  8. 1 1
      docs/Data_Ingestion.md
  9. BIN=BIN
      docs/Data_Ingestion.pdf
  10. 2 2
      docs/Final_Report.md
  11. BIN=BIN
      docs/Final_Report.pdf
  12. 1 1
      docs/Installation_Guide.md
  13. BIN=BIN
      docs/Installation_Guide.pdf
  14. 2 2
      docs/Operator_Installation_Guide.md
  15. BIN=BIN
      docs/Operator_Installation_Guide.pdf
  16. 1 1
      docs/Scrum_Artifacts.md
  17. BIN=BIN
      docs/Scrum_Artifacts.pdf
  18. 1 1
      docs/Scrum_Daily.md
  19. BIN=BIN
      docs/Scrum_Daily.pdf
  20. 1 1
      docs/Scrum_Plan.md
  21. BIN=BIN
      docs/Scrum_Plan.pdf
  22. 1 1
      docs/Scrum_Retro.md
  23. BIN=BIN
      docs/Scrum_Retro.pdf
  24. 1 1
      docs/Scrum_Review.md
  25. BIN=BIN
      docs/Scrum_Review.pdf
  26. 1 1
      docs/Scrum_Wiki.md
  27. BIN=BIN
      docs/Scrum_Wiki.pdf
  28. 1 1
      docs/Start_Stop_Procedures.md
  29. BIN=BIN
      docs/Start_Stop_Procedures.pdf
  30. 18 10
      docs/Technical_Document.md
  31. BIN=BIN
      docs/Technical_Document.pdf
  32. 1 1
      docs/Test_Cases_Sprint8.md
  33. BIN=BIN
      docs/Test_Cases_Sprint8.pdf
  34. 1 1
      docs/User_Description.md
  35. BIN=BIN
      docs/User_Description.pdf
  36. 2 2
      docs/User_Guide.md
  37. BIN=BIN
      docs/User_Guide.pdf
  38. 1 1
      docs/WSL_Deployment.md
  39. BIN=BIN
      docs/WSL_Deployment.pdf
  40. 1 1
      docs/Wiki_Home.md
  41. BIN=BIN
      docs/Wiki_Home.pdf
  42. BIN=BIN
      docs/architecture.pdf
  43. BIN=BIN
      docs/disaster_recovery_plan.pdf
  44. BIN=BIN
      docs/distributed_deployment.pdf
  45. BIN=BIN
      docs/docker_connection.pdf
  46. BIN=BIN
      docs/project_report.pdf
  47. BIN=BIN
      docs/retro_planning.pdf
  48. BIN=BIN
      docs/taiga_audit_report.pdf
  49. BIN=BIN
      docs/zabbix_monitoring.pdf
  50. 258 9
      generate_docs.py
  51. 1 1
      scripts/deploy_to_server.py
  52. 3 3
      scripts/manage_models.sh
  53. 1 1
      snmp_notifier.py

BIN=BIN
Project.pdf


+ 2 - 1
README.md

@@ -41,7 +41,8 @@ This project leverages specialized AI skills to maintain code quality, documenta
 - **Test Generator**: Generates comprehensive unit and integration tests focusing on boundary conditions and logical coverage.
 
 ## Grading
-There will be 6 grades in total: 3 for Project Management 1 (PM1) and 3 for Domain-specifc Project 1 (DSP1).
+
+There will be 6 grades in total: 3 for Project Management 1 (PM1) and 3 for Domain-specific Project 1 (DSP1).
 
 ### PM1:
 * Requirements analysis and assessment.

BIN=BIN
Retro Planning.pdf


+ 70 - 68
app.py

@@ -35,7 +35,7 @@ def get_active_model() -> str:
         load_dotenv(dotenv_path=env_path, override=True)
     except Exception:
         pass
-    return os.environ.get('LLM_MODEL', 'llama3.2-vision:11b')
+    return os.environ.get('LLM_MODEL', 'llama3.2:3b')
 
 ACTIVE_MODEL = get_active_model()
 
@@ -45,78 +45,80 @@ def strip_scratchpad(text: str) -> str:
     clean_text = re.sub(r'<scratchpad>.*?</scratchpad>', '', text, flags=re.DOTALL)
     return clean_text.strip()
 
+@st.cache_data(show_spinner=False)
 def detect_allergens_from_text(name: str, ingredients: str) -> set:
     import re
+    import ollama
     detected = set()
-    text = (name + " " + ingredients).lower()
-    mappings = {
-        "peanut": "Peanuts",
-        "cacahuète": "Peanuts",
-        "cacahuete": "Peanuts",
-        "egg": "Eggs",
-        "oeuf": "Eggs",
-        "œuf": "Eggs",
-        "milk": "Milk",
-        "lait": "Milk",
-        "butter": "Milk",
-        "beurre": "Milk",
-        "cheese": "Milk",
-        "fromage": "Milk",
-        "cream": "Milk",
-        "crème": "Milk",
-        "creme": "Milk",
-        "wheat": "Wheat",
-        "blé": "Wheat",
-        "ble": "Wheat",
-        "gluten": "Gluten",
-        "soy": "Soy",
-        "soja": "Soy",
-        "almond": "Tree Nuts",
-        "amande": "Tree Nuts",
-        "cashew": "Tree Nuts",
-        "walnut": "Tree Nuts",
-        "noix": "Tree Nuts",
-        "hazelnut": "Tree Nuts",
-        "noisette": "Tree Nuts",
-        "pecan": "Tree Nuts",
-        "pistachio": "Tree Nuts",
-        "pistache": "Tree Nuts",
-        "fish": "Fish",
-        "poisson": "Fish",
-        "salmon": "Fish",
-        "saumon": "Fish",
-        "tuna": "Fish",
-        "thon": "Fish",
-        "shrimp": "Shellfish",
-        "crevette": "Shellfish",
-        "crab": "Shellfish",
-        "crabe": "Shellfish",
-        "lobster": "Shellfish",
-        "homard": "Shellfish",
-        "mussel": "Shellfish",
-        "moule": "Shellfish",
-        "oyster": "Shellfish",
-        "huître": "Shellfish",
-        "huitre": "Shellfish",
-        "sesame": "Sesame",
-        "sésame": "Sesame",
-        "mustard": "Mustard",
-        "moutarde": "Mustard",
-        "celery": "Celery",
-        "céleri": "Celery",
-        "celeri": "Celery",
-        "lupin": "Lupin",
-        "mollusc": "Molluscs",
-        "mollusque": "Molluscs",
-        "sulphite": "Sulphites",
-        "sulfite": "Sulphites"
-    }
-    for keyword, allergen in mappings.items():
-        pattern = r'\b' + re.escape(keyword) + r's?\b'
-        if re.search(pattern, text):
-            detected.add(allergen)
+    
+    # Extract candidate terms from name and ingredients
+    candidates = []
+    if ingredients:
+        # Split by typical separators: commas, semicolons, parentheses, newlines
+        parts = re.split(r'[,;()\[\]\n\r]', ingredients)
+        for p in parts:
+            p_clean = re.sub(r'[*_\d%]+', '', p).strip()
+            # Remove empty or common placeholder ingredients/non-ingredients
+            if len(p_clean) > 2 and p_clean.lower() not in ['ingredients', 'and', 'contains', 'may contain', 'natural', 'artificial', 'flavors', 'flavor', 'preservative', 'color', 'colors']:
+                candidates.append(p_clean)
+                
+    if name:
+        name_clean = re.sub(r'[*_\d%]+', '', name).strip()
+        if len(name_clean) > 2:
+            candidates.append(name_clean)
+            for word in re.split(r'\s+', name_clean):
+                w_clean = word.strip()
+                if len(w_clean) > 2 and w_clean.lower() not in ['with', 'and', 'for', 'the', 'bar', 'cup', 'can', 'bag', 'mix']:
+                    candidates.append(w_clean)
+                    
+    # Deduplicate candidates while keeping order
+    seen = set()
+    unique_candidates = []
+    for c in candidates:
+        c_low = c.lower()
+        if c_low not in seen:
+            seen.add(c_low)
+            unique_candidates.append(c)
+            
+    if not unique_candidates:
+        return detected
+        
+    prompt_lines = [
+        "You are a food safety expert. For each item in the list below, answer the question exactly.",
+        "Respond with 'Yes' or 'No'. Format the output exactly as:",
+        "ItemName: Yes/No",
+        "\nQuestions:"
+    ]
+    for c in unique_candidates:
+        prompt_lines.append(f"Answer by yes or no, if it is in some case answer yes : Are {c} allergens.")
+        
+    prompt = "\n".join(prompt_lines)
+    
+    try:
+        response = ollama.chat(model=get_active_model(), messages=[
+            {'role': 'user', 'content': prompt}
+        ])
+        content = response['message']['content']
+        for line in content.split('\n'):
+            if ':' in line:
+                parts = line.split(':')
+                left = parts[0].strip().lower()
+                right = parts[1].strip().lower()
+                
+                for c in unique_candidates:
+                    c_low = c.lower()
+                    if c_low in left and 'yes' in right:
+                        detected.add(c.title())
+            else:
+                for c in unique_candidates:
+                    c_low = c.lower()
+                    if f"are {c} allergens" in line.lower() and 'yes' in line.lower():
+                        detected.add(c.title())
+    except Exception:
+        pass
     return detected
 
+
 def filter_scratchpad_stream(stream, raw_accumulator=None):
     buffer = ""
     in_scratchpad = False

+ 1 - 1
configure_zabbix_alerts.py

@@ -1,6 +1,6 @@
 #ident "@(#)$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
 import json
-#ident "@(#)$Format:LocalFoodAI:configure_zabbix_alerts.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
+#ident "@(#)$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
 import urllib.request
 import os
 

+ 1 - 1
docs/Backup_Procedure.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Database Backup and Restore Procedure
 
 ## 1. Overview & Policy

BIN=BIN
docs/Backup_Procedure.pdf


+ 1 - 1
docs/Data_Ingestion.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Data Ingestion Pipeline
 
 ## Overview

BIN=BIN
docs/Data_Ingestion.pdf


+ 2 - 2
docs/Final_Report.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Final Project Report (Living Document)
 
 ## What Has Been Done
@@ -6,7 +6,7 @@
 2. **Database Optimization**: Successfully loaded OpenFoodFacts records and utilized advanced vertical partitioning and FULLTEXT indices.
 3. **Clinical Subquery Strategy**: Refactored the core Pandas/SQL query pipeline to use subquery limiting, resolving Cartesian join explosions and reducing query latency to ~0.04s.
 4. **Monitoring & Security**: Nginx securely proxies traffic on Port 80. Zabbix actively monitors proxy and server health, dynamically handling SNMP/alert loops in local/offline fallback mode.
-5. **Git Versioning**: Implemented Git `.gitattributes` to push `$Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $` tracking directly into the Python Application UI.
+5. **Git Versioning**: Implemented Git `.gitattributes` to push `$Id$` tracking directly into the Python Application UI.
 
 ## What Needs To Be Done (Day 2 Operations)
 1. **SSL/TLS Certificates**: The Nginx proxy is functional on HTTP port 80. Port 443 (HTTPS) must be configured with a Let's Encrypt certificate for true production encryption.

BIN=BIN
docs/Final_Report.pdf


+ 1 - 1
docs/Installation_Guide.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Installation Guide
 
 ## Requirements

BIN=BIN
docs/Installation_Guide.pdf


+ 2 - 2
docs/Operator_Installation_Guide.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Local Food AI - Detailed Operator Installation Guide
 
 This document is a step-by-step installation, mapping, configuration, and verification manual for deploying the **Local Food AI** system in an enterprise environment. It covers hybrid hypervisor infrastructure (WSL2, Hyper-V, and VirtualBox), cross-node networking, SNMPv3 monitoring, alert channels, and acceptance testing.
@@ -179,6 +179,6 @@ Run these test cases to verify the installation:
 | :--- | :--- | :--- | :---: |
 | **TC-OP-01** | Search 'Cheese' on Search Tab | 10+ records returned in <0.04s. Listeria warning flags on unpasteurized. | `[ ]` |
 | **TC-OP-02** | Enter '1.5 cups' in Plate Tab | Parsed and converted to metric grams based on density index. | `[ ]` |
-| **TC-OP-03** | Ask Chat: 'Can I eat sushi?' | llama3.2-vision:11b retrieves database context and flags raw fish as forbidden for pregnancy. | `[ ]` |
+| **TC-OP-03** | Ask Chat: 'Can I eat sushi?' | llama3.2:3b retrieves database context and flags raw fish as forbidden for pregnancy. | `[ ]` |
 | **TC-OP-04** | Trigger manual db backup | Timestamped compressed .sql.gz created inside backups/ folder. | `[ ]` |
 | **TC-OP-05** | Terminate Ollama Container | Zabbix PROBLEM active alert generated on dashboard in < 30 seconds. | `[ ]` |

BIN=BIN
docs/Operator_Installation_Guide.pdf


+ 1 - 1
docs/Scrum_Artifacts.md

@@ -1,3 +1,3 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Scrum Artifacts
 Contains User Stories, velocity tracking, and burndown charts from Taiga.

BIN=BIN
docs/Scrum_Artifacts.pdf


+ 1 - 1
docs/Scrum_Daily.md

@@ -1,3 +1,3 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Daily Scrums
 - **26.05.07 DAILY**: Fixed time scope bug, added Nginx proxy, built sync scripts.

BIN=BIN
docs/Scrum_Daily.pdf


+ 1 - 1
docs/Scrum_Plan.md

@@ -1,3 +1,3 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Sprint Plans
 - **Sprint 10 PLAN**: Fix LLM Tool Calling, optimize Cartesian SQL explosion, build Teams webhooks.

BIN=BIN
docs/Scrum_Plan.pdf


+ 1 - 1
docs/Scrum_Retro.md

@@ -1,3 +1,3 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Sprint Retrospectives
 - **Sprint 10 RETROSPECTIVE**: Mitigated dirty data duplicates using SQL `GROUP BY`. Need to maintain strict Git commit tagging (`TG-XXX`).

BIN=BIN
docs/Scrum_Retro.pdf


+ 1 - 1
docs/Scrum_Review.md

@@ -1,3 +1,3 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Sprint Reviews
 - **Sprint 10 REVIEW**: App executes sub-second searches. Nginx fully operational on Port 80.

BIN=BIN
docs/Scrum_Review.pdf


+ 1 - 1
docs/Scrum_Wiki.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Scrum Wiki Master List & Index Portal
 
 Welcome to the static Scrum documentation portal. This master wiki aggregates and organizes all daily stand-up logs, planning reports, retrospectives, reviews, and velocity charts recorded during the agile development of the **Local Food AI** clinical dietetics engine.

BIN=BIN
docs/Scrum_Wiki.pdf


+ 1 - 1
docs/Start_Stop_Procedures.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Infrastructure Stop & Start Operational Procedures
 
 This runbook outlines the exact sequence and commands to start, stop, and verify each microservice in the Local Food AI environment.

BIN=BIN
docs/Start_Stop_Procedures.pdf


+ 18 - 10
docs/Technical_Document.md

@@ -1,5 +1,5 @@
-#ident "@(#)$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
-# Local Food AI - Capstone Technical Document
+# $Id$
+Local Food AI - Capstone Technical Document
 
 This document provides a comprehensive technical overview of the **Local Food AI** system. It details the installation and configuration procedures, technologies used, Antigravity agent usage/permissions, agent engineering reflections, local LLM design decisions, local microservice component communication, and data privacy verification.
 
@@ -32,19 +32,26 @@ flowchart TD
     end
 
     subgraph "Gateway & Application Nodes"
-        Nginx["Nginx Reverse Proxy\n(Port 80)"]
-        Streamlit["Streamlit Web App\n(Port 8502 / Docker Container)"]
+        Nginx["Nginx Reverse Proxy
+(Port 80)"]
+        Streamlit["Streamlit Web App
+(Port 8502 / Docker Container)"]
     end
 
     subgraph "Intelligence & Search Nodes"
-        Ollama["Ollama Daemon\n(Port 11434 / Docker Container)"]
-        SearXNG["SearXNG Meta-Search\n(Port 8085 / Docker Container)"]
+        Ollama["Ollama Daemon
+(Port 11434 / Docker Container)"]
+        SearXNG["SearXNG Meta-Search
+(Port 8085 / Docker Container)"]
     end
 
     subgraph "Data Storage & Observability Nodes"
-        MySQL["MySQL Database Server\n(Port 3306 / Docker Container)"]
-        Zabbix["Zabbix Server & Agent\n(Ports 10051 & 10050)"]
-        ZabbixWeb["Zabbix Web Dashboard\n(Port 8081)"]
+        MySQL["MySQL Database Server
+(Port 3306 / Docker Container)"]
+        Zabbix["Zabbix Server & Agent
+(Ports 10051 & 10050)"]
+        ZabbixWeb["Zabbix Web Dashboard
+(Port 8081)"]
     end
 
     %% Communication paths
@@ -144,7 +151,8 @@ During the deployment and configuration phases, the Antigravity agent encountere
 ### 5.1 Regex Greediness Corrupting Python Literals
 * **The Struggle**: The dynamic git filter `git-ident-filter.py` used a greedy wildcard matching pattern `.*?[^$]*?$` which matched across lines. During checkouts, this matched from the `$Format:` string literal on line 403 of `app.py` directly to the regex search string on line 404, corrupting the code block into a single invalid tag and triggering a `SyntaxError: unterminated string literal`.
 * **The Resolution**:
-  1. We modified the pattern in the filter to be line-restricted (`[^\r\n$]+\$`), ensuring it never matches across newline boundaries.
+  1. We modified the pattern in the filter to be line-restricted (`[^
+$]+\$`), ensuring it never matches across newline boundaries.
   2. We split the string literal searches inside `app.py` so they are physically split across concatenated strings (e.g. `"$Form" + "at:"`), which prevents the filter from ever matching the source code strings.
 
 ### 5.2 Git Checkout Filter Self-Mod Loops

BIN=BIN
docs/Technical_Document.pdf


+ 1 - 1
docs/Test_Cases_Sprint8.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Sprint 8 Legacy Test Cases
 - Tested RAG AI tool integration.
 - Tested user authentication flows.

BIN=BIN
docs/Test_Cases_Sprint8.pdf


+ 1 - 1
docs/User_Description.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Local Food AI - User Description & Functional Guide
 
 ## 1. System Vision

BIN=BIN
docs/User_Description.pdf


+ 2 - 2
docs/User_Guide.md

@@ -1,5 +1,5 @@
-#ident "@(#)$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
-# Local Food AI - Clinician User Manual
+# $Id$
+Local Food AI - Clinician User Manual
 
 Welcome to the **Local Food AI** clinical dietitian explorer. This guide explains how to use the platform to search for products, build custom recipe plates, calculate cumulative nutritional statistics, and consult the privacy-safe AI assistant.
 

BIN=BIN
docs/User_Guide.pdf


+ 1 - 1
docs/WSL_Deployment.md

@@ -1,4 +1,4 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # WSL Deployment Runbook
 To deploy on Windows Subsystem for Linux:
 1. Ensure WSL2 backend is enabled in Docker Desktop.

BIN=BIN
docs/WSL_Deployment.pdf


+ 1 - 1
docs/Wiki_Home.md

@@ -1,3 +1,3 @@
-# $Id: 03cbc893f143c3ae43fc35e97913bedb89b41e23 Lange François lanfr144@school.lu 2026/06/11 10:38:26 Lange François lanfr144@school.lu 2026/06/11 10:38:26   [#1] chore: fix git-ident-filter self-modification regex bug by concatenating search strings [PreRelease-1.0-28-g03cbc89] $
+# $Id$
 # Documentation Home
 Welcome to the static documentation mirror. Please navigate the markdown files in this directory for architectural diagrams and guides.

BIN=BIN
docs/Wiki_Home.pdf


BIN=BIN
docs/architecture.pdf


BIN=BIN
docs/disaster_recovery_plan.pdf


BIN=BIN
docs/distributed_deployment.pdf


BIN=BIN
docs/docker_connection.pdf


BIN=BIN
docs/project_report.pdf


BIN=BIN
docs/retro_planning.pdf


BIN=BIN
docs/taiga_audit_report.pdf


BIN=BIN
docs/zabbix_monitoring.pdf


+ 258 - 9
generate_docs.py

@@ -3,7 +3,7 @@
 # $Author$
 # $log$
 import os
-#ident "@(#)$Format:LocalFoodAI:generate_docs.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
+#ident "@(#)$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
 import subprocess
 
 docs_dir = "docs"
@@ -141,17 +141,266 @@ Drop a `en.openfoodfacts.org.products.csv` file into the `/data` folder and run
 5. Navigate to `http://localhost` (or `http://localhost:8502` for direct Streamlit port)
 """,
     "User_Guide.md": """# $Id$
-# User Guide
+Local Food AI - Clinician User Manual
+
+Welcome to the **Local Food AI** clinical dietitian explorer. This guide explains how to use the platform to search for products, build custom recipe plates, calculate cumulative nutritional statistics, and consult the privacy-safe AI assistant.
+
+---
+
+## 1. Accessing the Application
+
+To access the platform on your local network:
+1. Open your web browser (Chrome, Firefox, or Safari).
+2. Enter the host address provided by your IT administrator (e.g., `http://192.168.130.170:8502/` or `http://localhost:8502/`).
+3. You will be greeted by the secure login screen.
 
-## 1. Clinical Data Search
-Search for products using keywords. The system utilizes FULLTEXT matching to instantly return the top 10 relevant matches alongside macronutrient data.
+---
+
+## 2. Account Login & Security
+
+To protect patient information, the system requires credentials:
+* **Login**: Enter your standard clinician username and password.
+* **Request Reset**: If you have forgotten your password, select **Reset Password** in the sidebar. Enter your username, and a secure password recovery link will be dispatched to your registered email.
+* **Active Session**: The application uses secure local browser cookies to retain your login session for a convenient experience. Select **Logout** in the sidebar at any time to terminate your session.
+
+---
 
-## 2. My Plate Builder
-Add portion sizes of different foods to calculate cumulative nutritional intake. Use the 🗑️ icon to remove items.
+## 3. Sidebar Features & Controls
+
+The left-hand sidebar houses several global settings:
+* **Network Status**: Visual indicator of whether you are in *Online/Server* mode or *Offline/Local Fallback* mode.
+* **LLM Engine Status**: Displays the active local AI model being queried (e.g., `llama3.2:3b`).
+* **Active User Info**: Shows the logged-in clinician profile.
+* **Dynamic Version Header**: Displays the system Git version, date, and commit code for auditable change management.
+
+---
+
+## 4. Feature Guides
+
+The application dashboard is split into three interactive workspace tabs:
+
+### 4.1. Clinical Data Search Tab 🔍
+Use this tab to browse the local OpenFoodFacts food database.
+1. **Keyword Input**: Type a product name, brand, or barcode (e.g., "whole wheat bread" or "unpasteurized cheese").
+2. **Dynamic Results**: The database performs a rapid search, displaying the top 10 matched products.
+3. **Nutritional Score**: Shows the Nutri-Score grade (A to E) and details (Proteins, Carbs, Fats, Energy in kcal) per 100g.
+4. **Allergen Warnings**: Shows highlight flags if the product contains common allergens matching your client's needs.
+
+### 4.2. My Plate Builder Tab 🍽️
+Build custom meals or recipe portions to calculate total client nutritional intake.
+1. **Adding Items**: When browsing foods in the Search Tab, click **Add to Plate**.
+2. **Specifying Portions**: Input the quantity using either decimal weights (in grams) or common volume descriptors (e.g., "1.5 cups", "2 tablespoons"). The converter translates volume to metric weight based on the product density.
+3. **Cumulative Intake Table**: The tab renders a table summarizing individual macros and total energy.
+4. **Visual Metrics**: Renders a dynamic bar chart comparing Carbs, Proteins, and Fats against recommended clinical intake thresholds.
+5. **Editing the Plate**: Use the trash bin icon (🗑️) to instantly remove any item from the calculation.
+
+### 4.3. Consultation Chat Tab 💬
+Consult the built-in clinical AI dietitian assistant for recipe validation, medical profile warnings, and meal plans.
+1. **Client Profile Selection**: Select active dietary constraints (e.g., pregnancy, diabetes, kidney disease, vegetarian) in the dropdown.
+2. **Asking Questions**: Type your prompt (e.g., "Is unpasteurized brie cheese safe for a pregnant client?" or "Design a low-sodium, high-protein menu").
+3. **RAG-Augmented Output**: The local AI assistant automatically searches the SQL database to fetch exact ingredient and macro rows before writing its response.
+4. **Chain-of-Thought Explanation**: The AI displays its reasoning process step-by-step to explain how it formulated the final diet recommendation or safety warning.
+
+---
 
-## 3. Chat with AI
-Ask the `qwen2.5:7b` model complex dietary questions. It natively utilizes RAG Tool Calling to silently search the database and formulate clinical answers.
+## 5. Privacy and Offline Support
+
+Because patient privacy is critical:
+* **No Cloud Overhead**: All search strings, chat prompts, and plate records are processed locally inside the host node.
+* **Safe External Searches**: When asking about foods not indexed in the database, the AI queries a local private search wrapper (SearXNG) that strips metadata and cookies, ensuring no identifying queries are sent to external web engines.
 """,
+
+
+
+        "Technical_Document.md": """# $Id$
+Local Food AI - Capstone Technical Document
+
+This document provides a comprehensive technical overview of the **Local Food AI** system. It details the installation and configuration procedures, technologies used, Antigravity agent usage/permissions, agent engineering reflections, local LLM design decisions, local microservice component communication, and data privacy verification.
+
+---
+
+## 1. System Overview & Technologies Used
+
+The Local Food AI system is a privacy-first, locally-hosted clinical dietitian platform. It is designed to run in environments with strict network restrictions (such as clinics or hospitals) while delivering sub-second database lookups and medical advice.
+
+### Technology Stack
+* **Frontend Web UI**: Streamlit (Python) - hosts search tabs, plate builder, and RAG chat portal.
+* **Database**: MySQL 8.0 - stores OpenFoodFacts records with dynamic vertical partitioning.
+* **Database Migrations**: Alembic - automates schema migrations and relational view definitions.
+* **AI NLP Inference Engine**: Ollama (locally hosted daemon) - runs quantized local models.
+* **Private Web Meta-Search**: SearXNG - provides anonymous web search fallback without cookies or tracking.
+* **Observability Suite**: Zabbix (Server, Web UI, and Agent) - captures SNMP telemetry, custom application traps, and status loops.
+* **Web Server Proxy Gateway**: Nginx - acts as a secure reverse proxy on standard network Port 80.
+* **Task Pipelines**: Apache Airflow - schedules and monitors data ingestion flows.
+
+---
+
+## 2. Dynamic Component Infrastructure Diagram
+
+The diagram below represents how the system components communicate locally inside the closed network boundary. All request-response loops are processed within the host server limits.
+
+```mermaid
+flowchart TD
+    subgraph "Client Layer"
+        Browser["Clinician Browser"]
+    end
+
+    subgraph "Gateway & Application Nodes"
+        Nginx["Nginx Reverse Proxy\n(Port 80)"]
+        Streamlit["Streamlit Web App\n(Port 8502 / Docker Container)"]
+    end
+
+    subgraph "Intelligence & Search Nodes"
+        Ollama["Ollama Daemon\n(Port 11434 / Docker Container)"]
+        SearXNG["SearXNG Meta-Search\n(Port 8085 / Docker Container)"]
+    end
+
+    subgraph "Data Storage & Observability Nodes"
+        MySQL["MySQL Database Server\n(Port 3306 / Docker Container)"]
+        Zabbix["Zabbix Server & Agent\n(Ports 10051 & 10050)"]
+        ZabbixWeb["Zabbix Web Dashboard\n(Port 8081)"]
+    end
+
+    %% Communication paths
+    Browser -->|HTTP| Nginx
+    Nginx -->|Reverse Proxy Pass| Streamlit
+    Streamlit -->|EAV & FULLTEXT SQL queries| MySQL
+    Streamlit -->|Local Chat Inference / RAG| Ollama
+    Streamlit -->|Tool-Calling search queries| SearXNG
+    Streamlit -->|SNMP Traps / Telemetry| Zabbix
+    ZabbixWeb -->|Queries metrics| Zabbix
+```
+
+---
+
+## 3. Installation & Configuration Guide
+
+To deploy the Local Food AI system, follow the sequential commands below:
+
+### 3.1 Prerequisite Environment Setup
+The notebook workstation must have at least 16 GB of RAM, Docker, and Docker Compose installed.
+
+### 3.2 Dynamic Double-Mode Configuration
+1. **Host Environment File (`.env`)**:
+   Configure database credentials, active network mode, and the target model name:
+   ```ini
+   NETWORK_MODE=server
+   LLM_MODEL=llama3.2:3b
+   MYSQL_ROOT_PASSWORD=your_db_password_here
+   DB_READER_PASS=your_db_password_here
+   DB_LOADER_PASS=your_db_password_here
+   DB_APP_AUTH_PASS=your_db_password_here
+   MYSQL_ZABBIX_PASSWORD=your_db_password_here
+   SERVER_HOST=192.168.130.170
+   SERVER_USER=francois
+   SERVER_PASS=your_db_password_here
+   ```
+
+2. **Compose Topology Mappings**:
+   The `app` container maps the host's `.env` config file dynamically using environment bindings and volume mounts inside [docker-compose.yml](file:///c:/Users/lanfr144/Documents/DOPRO1/Antigravity/Food/docker-compose.yml):
+   ```yaml
+     app:
+       build:
+         context: .
+         dockerfile: docker/app/Dockerfile
+       ports:
+         - "8502:8501"
+       environment:
+         - DB_HOST=mysql
+         - DB_USER=food_reader
+         - DB_PASS=${DB_READER_PASS}
+         - LLM_MODEL=${LLM_MODEL}
+       volumes:
+         - ./.env:/app/.env
+   ```
+
+### 3.3 Execution Commands
+* **Production Build & Launch**:
+  ```bash
+  docker compose up -d --build
+  ```
+* **Offline Local Fallback Build & Launch**:
+  ```bash
+  docker compose -f docker-compose_skip.yml up -d --build
+  ```
+* **Sequential Shutdown & Restart (Safe Ordering)**:
+  Run the sequential operations script to prevent dependency hangs:
+  ```bash
+  chmod +x manage_services.sh
+  ./manage_services.sh restart
+  ```
+
+---
+
+## 4. Antigravity Models, Agent Tasks & Permissions
+
+During the capstone engineering lifecycle, specialized Antigravity models were utilized to orchestrate task domains. To maintain strict repository security, agent permissions were configured with the narrowest scope possible.
+
+### 4.1 Antigravity Models & Task Domains
+* **Code Review Subagent**: Analyzed pull requests and code modifications in `app.py`, identifying structural vulnerabilities and syntax errors.
+* **Doc Writer Subagent**: Maintained and generated the markdown manuals inside the `docs/` folder, ensuring they stayed synchronized with file changes.
+* **Expert Coach Subagent**: Guided architectural patterns, enforced optimal EAV vertical partitioning schemas in MySQL, and checked the validity of `$Format:` dynamic headers.
+* **Git Commit Governance Subagent**: Linked repository commits directly to the Taiga task board using strict Taiga hooks and validated task creation.
+* **SQL Optimizer Subagent**: Reviewed indices, FULLTEXT query structures, and partitioning tables to prevent Cartesian query time increases.
+
+### 4.2 Agent Permissions Configuration
+To restrict the agent's capability and protect the developer environment, permissions were set under the following restrictions:
+* **`read_file` & `write_file`**: Limited exclusively to the workspace directory `c:\\Users\\lanfr144\\Documents\\DOPRO1\\Antigravity\\Food` (excluding system-level directories like `/tmp` or `.gemini`).
+* **`command` (Shell Execution)**: Sandboxed to standard non-root terminal commands. Command prefixes were limited to `git`, `python`, `chmod`, `docker-compose`, and `Get-Content` within the workspace path.
+* **`read_url` & `execute_url`**: Restrained solely to local network nodes (`192.168.130.170` for docker orchestration and `192.168.130.161` for Taiga API requests) to prevent external DNS lookups or unauthorized egress.
+
+---
+
+## 5. Reflections: Engineering Struggles & Solutions
+
+During the deployment and configuration phases, the Antigravity agent encountered several technical struggles, which were successfully resolved as follows:
+
+### 5.1 Regex Greediness Corrupting Python Literals
+* **The Struggle**: The dynamic git filter `git-ident-filter.py` used a greedy wildcard matching pattern `.*?[^$]*?$` which matched across lines. During checkouts, this matched from the `$Format:` string literal on line 403 of `app.py` directly to the regex search string on line 404, corrupting the code block into a single invalid tag and triggering a `SyntaxError: unterminated string literal`.
+* **The Resolution**:
+  1. We modified the pattern in the filter to be line-restricted (`[^\r\n$]+\$`), ensuring it never matches across newline boundaries.
+  2. We split the string literal searches inside `app.py` so they are physically split across concatenated strings (e.g. `"$Form" + "at:"`), which prevents the filter from ever matching the source code strings.
+
+### 5.2 Git Checkout Filter Self-Mod Loops
+* **The Struggle**: When performing cache resets or major checkouts, Git deleted `local_tools/git-ident-filter.py` from the disk. When git began restoring other files, it attempted to call the smudge filter, but since the script was missing, Python threw file-not-found errors and checkouts failed.
+* **The Resolution**: We separated the checkout process by checking out the filter script first (`git checkout HEAD -- local_tools/git-ident-filter.py`), and then executing checkout on the rest of the repository.
+
+### 5.3 Character Encoding Conflicts
+* **The Struggle**: French accent characters (such as `ç` in `Lange François`) in the smudged Git headers were written using different system encoding tables. Python's default text readers choked on these characters with decode errors, blocking file writes.
+* **The Resolution**: We built custom Python encoding sanitizer scripts that opened markdown and python files with `errors='replace'`, stripped out replacement characters, and forced them to overwrite as clean UTF-8 strings.
+
+---
+
+## 6. Local LLM Rationale
+
+The Local Food AI system is configured to run **`llama3.2:3b`** (quantized 3-Billion parameter Llama 3.2 model) natively using Ollama.
+
+### Rationale
+1. **Hardware Memory Footprint**: The model utilizes 4-bit quantization, requiring roughly 2.2 GB of RAM. This fits comfortably inside the minimal hardware constraint (16 GB total notebook memory) alongside the MySQL and Zabbix containers.
+2. **Clinical Dialogue Proficiency**: Despite its small size, Llama 3.2 is highly optimized for instruction-following and tool-calling. This allows the Streamlit app to reliably execute RAG lookups (generating SQL queries or meta-search requests) and format responses using clinical CoT templates.
+3. **Completely Local Inference**: The model runs entirely inside the `food-ollama-1` container on the local network, bypassing any latency or dependency associated with commercial cloud models.
+
+---
+
+## 7. Data Privacy Verification: Keeping User Data on the Server
+
+To prove and guarantee that no clinical user details or dietary profiles leave the local server boundary, we executed the following verification procedures:
+
+1. **Proxy Access Log Audits**:
+   Audited Nginx (`/var/log/nginx/access.log`) and Streamlit access logs. All connections originate exclusively from local subnet IPs (e.g., `192.168.1.50` or loopback `127.0.0.1`).
+2. **Network Egress Block (Docker Configuration)**:
+   The `mysql` and `app` services inside `docker-compose.yml` run inside a custom bridge network. The database container has no external port bindings to the public internet, and the `app` container only exposes port `8502` to the local LAN.
+3. **Private Web Meta-Search (SearXNG)**:
+   The SearXNG meta-search container redirects external queries locally. Standard search APIs route traffic anonymously through local proxy rotators to prevent search engines from linking queries to the clinician's IP or user profile.
+4. **Traffic Sniffing (TCPDump Verification)**:
+   We ran `tcpdump` on the server interface during active chat sessions:
+   ```bash
+   tcpdump -i eth0 dst port not 80 and dst port not 22 and dst port not 161
+   ```
+   No packet transmissions were detected routing data outside the local network, proving that LLM prompts, dietitian responses, and plate nutritional configurations remain entirely inside the local node boundary.
+""",
+
+
     "Wiki_Home.md": """# $Id$
 # Documentation Home
 Welcome to the static documentation mirror. Please navigate the markdown files in this directory for architectural diagrams and guides.
@@ -537,7 +786,7 @@ Run these test cases to verify the installation:
 | :--- | :--- | :--- | :---: |
 | **TC-OP-01** | Search 'Cheese' on Search Tab | 10+ records returned in <0.04s. Listeria warning flags on unpasteurized. | `[ ]` |
 | **TC-OP-02** | Enter '1.5 cups' in Plate Tab | Parsed and converted to metric grams based on density index. | `[ ]` |
-| **TC-OP-03** | Ask Chat: 'Can I eat sushi?' | llama3.2-vision:11b retrieves database context and flags raw fish as forbidden for pregnancy. | `[ ]` |
+| **TC-OP-03** | Ask Chat: 'Can I eat sushi?' | llama3.2:3b retrieves database context and flags raw fish as forbidden for pregnancy. | `[ ]` |
 | **TC-OP-04** | Trigger manual db backup | Timestamped compressed .sql.gz created inside backups/ folder. | `[ ]` |
 | **TC-OP-05** | Terminate Ollama Container | Zabbix PROBLEM active alert generated on dashboard in < 30 seconds. | `[ ]` |
 """

+ 1 - 1
scripts/deploy_to_server.py

@@ -34,7 +34,7 @@ def deploy():
         ssh.connect(host, username=user, password=password, timeout=10)
         print("Connected successfully!")
         
-        local_model = os.environ.get('LLM_MODEL', 'llama3.2-vision:11b')
+        local_model = os.environ.get('LLM_MODEL', 'llama3.2:3b')
         command = f"cd food_project && git stash && rm -f git_version.txt git_id.txt && git pull && git stash clear && sed -i 's/^LLM_MODEL=.*/LLM_MODEL={local_model}/' .env && docker-compose up -d --build"
         print(f"Executing: {command}")
         

+ 3 - 3
scripts/manage_models.sh

@@ -1,12 +1,12 @@
 #!/bin/bash
 #ident "@(#)$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
 
-echo "Pulling the new efficient billion-parameter model (llama3.2-vision:11b)..."
-docker exec food-ollama-1 ollama pull llama3.2-vision:11b
+echo "Pulling the new efficient billion-parameter model (llama3.2:3b)..."
+docker exec food-ollama-1 ollama pull llama3.2:3b
 
 echo "Cleaning up unused models to free up disk space..."
 docker exec food-ollama-1 ollama rm qwen2.5:7b
-docker exec food-ollama-1 ollama rm llama3.2:3b
+docker exec food-ollama-1 ollama rm llama3.2-vision:11b
 
 echo "Currently installed models:"
 docker exec food-ollama-1 ollama list

+ 1 - 1
snmp_notifier.py

@@ -1,7 +1,7 @@
 #ident "@(#)$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
 import os
 import socket
-#ident "@(#)$Format:LocalFoodAI:snmp_notifier.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
+#ident "@(#)$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
 import socket
 
 class SNMPNotifier: