BMS Deployer commited on
Commit
9916f7c
·
0 Parent(s):

Remove header and fix inventory

Browse files
Files changed (44) hide show
  1. BMS_Demo_Pro.html +197 -0
  2. BMS_Documentation_For_Word.md +50 -0
  3. BMS_Presentation_Slides.md +56 -0
  4. BMS_Process_Flow_Pro.html +192 -0
  5. BMS_Tech_Specs_Pro.html +186 -0
  6. COMPLETE_PROCESS_FLOW.html +329 -0
  7. Dockerfile +19 -0
  8. README.md +19 -0
  9. STANDALONE_DEMO.html +468 -0
  10. TEAM_ANNOUNCEMENT_EMAIL.md +85 -0
  11. TECHNICAL_SPECIFICATION.html +603 -0
  12. app/config.py +9 -0
  13. app/data_loader.py +152 -0
  14. app/forecasting.py +43 -0
  15. app/intent_parser.py +102 -0
  16. app/llm_engine.py +98 -0
  17. app/llm_engine_backup.py +128 -0
  18. app/llm_engine_old.py +128 -0
  19. app/main.py +155 -0
  20. app/pdf_generator.py +72 -0
  21. data/company_context.txt +16 -0
  22. data/demand_history.csv +0 -0
  23. data/inventory.csv +323 -0
  24. data/items.csv +101 -0
  25. data/promotions.csv +22 -0
  26. data/requisitions.csv +12 -0
  27. data/suppliers.csv +11 -0
  28. data/warehouses.csv +7 -0
  29. requirements.txt +10 -0
  30. static/index.html +426 -0
  31. static/reports/forecast_BMS0001_20251130230015.pdf +0 -0
  32. static/reports/forecast_BMS0015_20251129164645.pdf +0 -0
  33. static/reports/forecast_FS1234_20251126172817.pdf +0 -0
  34. static/reports/forecast_FS1234_20251126173353.pdf +0 -0
  35. static/reports/forecast_FS1234_20251126175819.pdf +0 -0
  36. static/reports/forecast_FS1234_20251126180611.pdf +0 -0
  37. static/reports/forecast_FS1234_20251126180710.pdf +0 -0
  38. static/reports/forecast_FS1234_20251126181554.pdf +0 -0
  39. static/reports/forecast_FS1234_20251126182220.pdf +0 -0
  40. static/reports/forecast_FS1234_20251126183143.pdf +0 -0
  41. static/reports/forecast_FS1234_20251126184230.pdf +0 -0
  42. static/reports/forecast_TEST-ITEM_20251126172116.pdf +0 -0
  43. static/reports/report_20251126172116.pdf +0 -0
  44. static/reports/report_20251129165821.pdf +90 -1
BMS_Demo_Pro.html ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>BMS AI Assistant</title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ :root {
11
+ --primary: #D01010;
12
+ --bg: #F5F7FB;
13
+ --chat-bg: #FFFFFF;
14
+ --text: #1F2937;
15
+ --border: #E5E7EB;
16
+ }
17
+
18
+ body {
19
+ font-family: 'Inter', sans-serif;
20
+ background-color: var(--bg);
21
+ margin: 0;
22
+ display: flex;
23
+ justify-content: center;
24
+ height: 100vh;
25
+ color: var(--text);
26
+ }
27
+
28
+ .container {
29
+ width: 100%;
30
+ max-width: 1000px;
31
+ background: var(--chat-bg);
32
+ display: flex;
33
+ flex-direction: column;
34
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
35
+ }
36
+
37
+ .header {
38
+ background: var(--primary);
39
+ color: white;
40
+ padding: 20px;
41
+ font-weight: 600;
42
+ font-size: 1.1rem;
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 15px;
46
+ }
47
+
48
+ .header-icon {
49
+ width: 24px;
50
+ height: 24px;
51
+ background: rgba(255, 255, 255, 0.2);
52
+ border-radius: 4px;
53
+ padding: 4px;
54
+ }
55
+
56
+ .chat-area {
57
+ flex: 1;
58
+ padding: 30px;
59
+ overflow-y: auto;
60
+ display: flex;
61
+ flex-direction: column;
62
+ gap: 20px;
63
+ }
64
+
65
+ .message {
66
+ max-width: 80%;
67
+ padding: 15px 20px;
68
+ border-radius: 8px;
69
+ line-height: 1.5;
70
+ font-size: 0.95rem;
71
+ }
72
+
73
+ .bot {
74
+ background: #F3F4F6;
75
+ align-self: flex-start;
76
+ border-left: 4px solid var(--primary);
77
+ }
78
+
79
+ .user {
80
+ background: var(--primary);
81
+ color: white;
82
+ align-self: flex-end;
83
+ }
84
+
85
+ .input-area {
86
+ padding: 20px;
87
+ border-top: 1px solid var(--border);
88
+ display: flex;
89
+ gap: 10px;
90
+ }
91
+
92
+ input {
93
+ flex: 1;
94
+ padding: 12px 15px;
95
+ border: 1px solid var(--border);
96
+ border-radius: 4px;
97
+ font-family: inherit;
98
+ font-size: 1rem;
99
+ }
100
+
101
+ button {
102
+ background: var(--primary);
103
+ color: white;
104
+ border: none;
105
+ padding: 0 25px;
106
+ border-radius: 4px;
107
+ font-weight: 600;
108
+ cursor: pointer;
109
+ }
110
+
111
+ table {
112
+ width: 100%;
113
+ border-collapse: collapse;
114
+ margin-top: 15px;
115
+ background: white;
116
+ font-size: 0.9em;
117
+ }
118
+
119
+ th,
120
+ td {
121
+ padding: 8px 12px;
122
+ border: 1px solid #ddd;
123
+ text-align: left;
124
+ }
125
+
126
+ th {
127
+ background: #eee;
128
+ }
129
+ </style>
130
+ </head>
131
+
132
+ <body>
133
+ <div class="container">
134
+ <div class="header">
135
+ <div class="header-icon">
136
+ <svg viewBox="0 0 24 24" fill="white">
137
+ <path
138
+ d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z" />
139
+ </svg>
140
+ </div>
141
+ BMS AI Assistant
142
+ </div>
143
+ <div class="chat-area" id="chat">
144
+ <div class="message bot">
145
+ <strong>System Ready.</strong><br>
146
+ BMS AI Assistant initialized. Connected to inventory and forecasting modules.<br><br>
147
+ Available Commands:<br>
148
+ - Forecast Demand (Item Code)<br>
149
+ - Check Inventory<br>
150
+ - Supplier Lookup
151
+ </div>
152
+ </div>
153
+ <div class="input-area">
154
+ <input type="text" id="input" placeholder="Enter command..." autocomplete="off">
155
+ <button onclick="send()">Send</button>
156
+ </div>
157
+ </div>
158
+
159
+ <script>
160
+ const chat = document.getElementById('chat');
161
+ const input = document.getElementById('input');
162
+
163
+ input.addEventListener('keypress', (e) => { if (e.key === 'Enter') send(); });
164
+
165
+ function send() {
166
+ const val = input.value.trim();
167
+ if (!val) return;
168
+
169
+ append(val, 'user');
170
+ input.value = '';
171
+
172
+ setTimeout(() => {
173
+ let response = "Command not recognized. Please specify Item Code.";
174
+ if (val.toLowerCase().includes('forecast')) {
175
+ response = `<strong>Demand Forecast Generated</strong><br>Analysis for requested item:<br>
176
+ <table><tr><th>Date</th><th>Projected Qty</th></tr>
177
+ <tr><td>2025-12-01</td><td>152</td></tr>
178
+ <tr><td>2025-12-02</td><td>148</td></tr>
179
+ <tr><td>2025-12-03</td><td>155</td></tr></table>`;
180
+ } else if (val.toLowerCase().includes('inventory')) {
181
+ response = "<strong>Inventory Status</strong><br>Location: NA Warehouse<br>On Hand: 450 Units<br>Status: Optimal";
182
+ }
183
+ append(response, 'bot');
184
+ }, 600);
185
+ }
186
+
187
+ function append(html, type) {
188
+ const div = document.createElement('div');
189
+ div.className = `message ${type}`;
190
+ div.innerHTML = html;
191
+ chat.appendChild(div);
192
+ chat.scrollTop = chat.scrollHeight;
193
+ }
194
+ </script>
195
+ </body>
196
+
197
+ </html>
BMS_Documentation_For_Word.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BMS AI Assistant: System Documentation
2
+
3
+ ## 1. Executive Summary
4
+ The BMS AI Assistant is an enterprise-grade conversational interface designed to optimize supply chain operations. By leveraging local AI inference and deterministic forecasting models, the system provides instant access to critical business data—including demand forecasts, inventory levels, and supplier information—without relying on external cloud APIs.
5
+
6
+ ## 2. System Architecture
7
+
8
+ ### 2.1 High-Level Design
9
+ The system operates on a micro-service architecture containerized within Docker.
10
+
11
+ * **Presentation Layer:** HTML5/JavaScript frontend served via FastAPI.
12
+ * **Application Layer:** Python-based backend handling intent parsing and business logic.
13
+ * **Data Layer:** In-memory CSV data structures for high-performance retrieval.
14
+ * **Intelligence Layer:** Hybrid engine combining ARIMA (Statistical) and TinyLlama (Generative AI).
15
+
16
+ ### 2.2 Process Flow
17
+ 1. **Input:** User submits natural language query.
18
+ 2. **Parsing:** Regex engine extracts Intent (e.g., "Forecast") and Entities (e.g., "BMS0015").
19
+ 3. **Routing:** Request is directed to the appropriate module (Forecasting, Inventory, etc.).
20
+ 4. **Execution:** Module performs computation or data lookup.
21
+ 5. **Response:** Results are formatted into JSON and rendered by the frontend.
22
+
23
+ ## 3. Technical Specifications
24
+
25
+ | Component | Technology | Specification |
26
+ | :--- | :--- | :--- |
27
+ | **Backend** | Python / FastAPI | v0.104+, Async I/O |
28
+ | **Server** | Uvicorn | ASGI Standard |
29
+ | **Forecasting** | Statsmodels | ARIMA(1,1,1) |
30
+ | **LLM** | TinyLlama | 1.1B Parameters, Quantized |
31
+ | **Frontend** | HTML/JS | Zero-dependency, Vanilla JS |
32
+ | **Deployment** | Docker | Debian-based Python Slim Image |
33
+
34
+ ## 4. Key Capabilities
35
+
36
+ ### Demand Forecasting
37
+ * **Methodology:** Time-series analysis using historical sales data.
38
+ * **Output:** 30-day forward-looking demand schedule.
39
+ * **Accuracy:** Optimized via ARIMA parameter tuning.
40
+
41
+ ### Inventory Management
42
+ * **Scope:** Multi-location stock tracking.
43
+ * **Features:** Real-time "On Hand" vs "Allocated" visibility.
44
+
45
+ ### Reporting
46
+ * **Format:** PDF (Generated on-demand).
47
+ * **Content:** Chat transcripts, forecast tables, and system alerts.
48
+
49
+ ---
50
+ *Generated for BMS Project Team | Version 1.0*
BMS_Presentation_Slides.md ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Slide 1: Title Slide
2
+ **Title:** BMS AI Assistant
3
+ **Subtitle:** Enterprise Supply Chain Optimization System
4
+ **Footer:** Technical Overview | Version 1.0
5
+
6
+ ---
7
+
8
+ # Slide 2: Executive Summary
9
+ **Key Points:**
10
+ * **Objective:** Streamline access to supply chain data via natural language.
11
+ * **Solution:** Local AI Chatbot with integrated forecasting capabilities.
12
+ * **Value Proposition:** Zero external API costs, 100% data privacy, instant response time.
13
+
14
+ ---
15
+
16
+ # Slide 3: System Architecture
17
+ **Layers:**
18
+ 1. **Presentation:** Web-based UI (HTML5/JS).
19
+ 2. **API:** FastAPI Backend (Python).
20
+ 3. **Logic:** Intent Parser & Business Rules.
21
+ 4. **Data:** In-memory high-speed data store.
22
+
23
+ ---
24
+
25
+ # Slide 4: Core Capabilities
26
+ **Features:**
27
+ * **Demand Forecasting:** 30-day predictive analytics using ARIMA.
28
+ * **Inventory Tracking:** Real-time multi-warehouse stock visibility.
29
+ * **Supplier Intelligence:** Vendor lead times and contact details.
30
+ * **Automated Reporting:** On-demand PDF generation.
31
+
32
+ ---
33
+
34
+ # Slide 5: Technical Stack
35
+ **Technologies:**
36
+ * **Language:** Python 3.10
37
+ * **Framework:** FastAPI + Uvicorn
38
+ * **AI Model:** TinyLlama 1.1B (Local Inference)
39
+ * **Deployment:** Docker Container
40
+
41
+ ---
42
+
43
+ # Slide 6: Process Flow
44
+ **Steps:**
45
+ 1. **User Input:** Natural language query.
46
+ 2. **Intent Recognition:** Pattern matching algorithm.
47
+ 3. **Data Retrieval:** SQL/CSV lookup or Model Inference.
48
+ 4. **Response Generation:** JSON formatting and UI rendering.
49
+
50
+ ---
51
+
52
+ # Slide 7: Future Roadmap
53
+ **Next Steps:**
54
+ * Integration with live ERP database (SQL).
55
+ * Advanced multi-variate forecasting models.
56
+ * User authentication and role-based access control.
BMS_Process_Flow_Pro.html ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>BMS AI Assistant - Process Flow Architecture</title>
8
+ <style>
9
+ :root {
10
+ --primary: #D01010;
11
+ --dark: #111;
12
+ --light: #f4f4f4;
13
+ --border: #ddd;
14
+ }
15
+
16
+ body {
17
+ font-family: 'Segoe UI', Helvetica, Arial, sans-serif;
18
+ line-height: 1.6;
19
+ color: #333;
20
+ max-width: 1000px;
21
+ margin: 0 auto;
22
+ padding: 40px;
23
+ background-color: #fff;
24
+ }
25
+
26
+ h1 {
27
+ color: var(--dark);
28
+ border-bottom: 3px solid var(--primary);
29
+ padding-bottom: 10px;
30
+ margin-bottom: 40px;
31
+ }
32
+
33
+ .step {
34
+ display: flex;
35
+ margin-bottom: 30px;
36
+ position: relative;
37
+ }
38
+
39
+ .step-number {
40
+ flex: 0 0 50px;
41
+ font-size: 24px;
42
+ font-weight: bold;
43
+ color: var(--primary);
44
+ padding-top: 0;
45
+ }
46
+
47
+ .step-content {
48
+ flex: 1;
49
+ border-left: 2px solid var(--border);
50
+ padding-left: 20px;
51
+ padding-bottom: 20px;
52
+ }
53
+
54
+ .step:last-child .step-content {
55
+ border-left: none;
56
+ }
57
+
58
+ h3 {
59
+ margin-top: 0;
60
+ color: var(--dark);
61
+ }
62
+
63
+ .meta {
64
+ font-size: 0.9em;
65
+ color: #666;
66
+ margin-bottom: 10px;
67
+ background: #f9f9f9;
68
+ padding: 10px;
69
+ border-radius: 4px;
70
+ }
71
+
72
+ code {
73
+ display: block;
74
+ background: #2d2d2d;
75
+ color: #ccc;
76
+ padding: 15px;
77
+ border-radius: 4px;
78
+ font-family: Consolas, monospace;
79
+ font-size: 0.9em;
80
+ margin-top: 10px;
81
+ overflow-x: auto;
82
+ }
83
+
84
+ .arrow {
85
+ text-align: center;
86
+ color: #ccc;
87
+ font-size: 20px;
88
+ margin: 10px 0;
89
+ display: none;
90
+ /* Hidden in this layout */
91
+ }
92
+ </style>
93
+ </head>
94
+
95
+ <body>
96
+
97
+ <h1>BMS AI Assistant: End-to-End Process Flow</h1>
98
+
99
+ <div class="step">
100
+ <div class="step-number">01</div>
101
+ <div class="step-content">
102
+ <h3>User Input Acquisition</h3>
103
+ <div class="meta">
104
+ <strong>Component:</strong> Frontend (index.html)<br>
105
+ <strong>Trigger:</strong> User submits query via chat interface
106
+ </div>
107
+ <p>The user enters a natural language query regarding demand, inventory, or supplier data.</p>
108
+ <code>userInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') sendMessage(); });</code>
109
+ </div>
110
+ </div>
111
+
112
+ <div class="step">
113
+ <div class="step-number">02</div>
114
+ <div class="step-content">
115
+ <h3>API Payload Construction</h3>
116
+ <div class="meta">
117
+ <strong>Component:</strong> Client-Side Logic (JavaScript)<br>
118
+ <strong>Action:</strong> POST /api/chat
119
+ </div>
120
+ <p>Input is validated, sanitized, and packaged into a JSON payload for the backend API.</p>
121
+ <code>fetch('/api/chat', { method: 'POST', body: JSON.stringify({message: text}) });</code>
122
+ </div>
123
+ </div>
124
+
125
+ <div class="step">
126
+ <div class="step-number">03</div>
127
+ <div class="step-content">
128
+ <h3>Intent Recognition & Entity Extraction</h3>
129
+ <div class="meta">
130
+ <strong>Component:</strong> Intent Parser (intent_parser.py)<br>
131
+ <strong>Method:</strong> Regex / Pattern Matching
132
+ </div>
133
+ <p>The system analyzes the text string to determine the user's objective (Intent) and extracts specific
134
+ parameters (Item Codes, Dates, Locations).</p>
135
+ <code>parsed = parser.parse(message) # Output: {'intent': 'forecast', 'item': 'BMS0015'}</code>
136
+ </div>
137
+ </div>
138
+
139
+ <div class="step">
140
+ <div class="step-number">04</div>
141
+ <div class="step-content">
142
+ <h3>Business Logic Routing</h3>
143
+ <div class="meta">
144
+ <strong>Component:</strong> Main Controller (main.py)
145
+ </div>
146
+ <p>Based on the identified intent, the request is routed to the specific handler module (Forecasting,
147
+ Inventory, Supplier, or LLM).</p>
148
+ </div>
149
+ </div>
150
+
151
+ <div class="step">
152
+ <div class="step-number">05</div>
153
+ <div class="step-content">
154
+ <h3>Data Processing & Computation</h3>
155
+ <div class="meta">
156
+ <strong>Component:</strong> Backend Modules (forecasting.py, data_loader.py)
157
+ </div>
158
+ <p><strong>Scenario A (Forecast):</strong> ARIMA model generates 30-day demand prediction.<br>
159
+ <strong>Scenario B (Inventory):</strong> System queries CSV database for real-time stock levels.
160
+ </p>
161
+ <code>forecast_data = forecast_demand(item_code, horizon)</code>
162
+ </div>
163
+ </div>
164
+
165
+ <div class="step">
166
+ <div class="step-number">06</div>
167
+ <div class="step-content">
168
+ <h3>Response Serialization</h3>
169
+ <div class="meta">
170
+ <strong>Component:</strong> Response Formatter
171
+ </div>
172
+ <p>Computed data is structured into a standardized JSON format, including text summaries and raw data arrays
173
+ for visualization.</p>
174
+ <code>return {"intent": "forecast", "answer": "...", "data": [...]}</code>
175
+ </div>
176
+ </div>
177
+
178
+ <div class="step">
179
+ <div class="step-number">07</div>
180
+ <div class="step-content">
181
+ <h3>Client-Side Rendering</h3>
182
+ <div class="meta">
183
+ <strong>Component:</strong> Frontend UI
184
+ </div>
185
+ <p>The browser receives the JSON response and dynamically renders the chat bubble, data tables, and
186
+ interactive elements.</p>
187
+ </div>
188
+ </div>
189
+
190
+ </body>
191
+
192
+ </html>
BMS_Tech_Specs_Pro.html ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>BMS AI Assistant - Technical Specifications</title>
8
+ <style>
9
+ :root {
10
+ --primary: #D01010;
11
+ --header-bg: #333;
12
+ --light-bg: #f8f9fa;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Arial, sans-serif;
17
+ line-height: 1.6;
18
+ color: #333;
19
+ max-width: 1000px;
20
+ margin: 0 auto;
21
+ padding: 40px;
22
+ }
23
+
24
+ header {
25
+ border-bottom: 4px solid var(--primary);
26
+ margin-bottom: 40px;
27
+ padding-bottom: 20px;
28
+ }
29
+
30
+ h1 {
31
+ margin: 0;
32
+ color: var(--header-bg);
33
+ font-size: 2.5em;
34
+ }
35
+
36
+ h2 {
37
+ color: var(--primary);
38
+ margin-top: 40px;
39
+ border-bottom: 1px solid #ddd;
40
+ padding-bottom: 10px;
41
+ }
42
+
43
+ h3 {
44
+ color: #555;
45
+ margin-top: 25px;
46
+ }
47
+
48
+ table {
49
+ width: 100%;
50
+ border-collapse: collapse;
51
+ margin: 20px 0;
52
+ font-size: 0.95em;
53
+ }
54
+
55
+ th,
56
+ td {
57
+ padding: 12px 15px;
58
+ border: 1px solid #ddd;
59
+ text-align: left;
60
+ }
61
+
62
+ th {
63
+ background-color: var(--header-bg);
64
+ color: white;
65
+ font-weight: 600;
66
+ }
67
+
68
+ tr:nth-child(even) {
69
+ background-color: var(--light-bg);
70
+ }
71
+
72
+ .tech-grid {
73
+ display: grid;
74
+ grid-template-columns: repeat(2, 1fr);
75
+ gap: 20px;
76
+ }
77
+
78
+ .tech-box {
79
+ background: var(--light-bg);
80
+ padding: 20px;
81
+ border-left: 4px solid var(--primary);
82
+ }
83
+
84
+ .tech-box h4 {
85
+ margin-top: 0;
86
+ color: var(--primary);
87
+ }
88
+
89
+ ul {
90
+ margin: 0;
91
+ padding-left: 20px;
92
+ }
93
+ </style>
94
+ </head>
95
+
96
+ <body>
97
+
98
+ <header>
99
+ <h1>BMS AI Assistant</h1>
100
+ <p><strong>System Technical Specifications</strong> | Version 1.0</p>
101
+ </header>
102
+
103
+ <h2>1. System Overview</h2>
104
+ <p>The BMS AI Assistant is a specialized conversational interface designed for supply chain optimization. It
105
+ integrates deterministic forecasting models with natural language processing to provide real-time decision
106
+ support for inventory and procurement operations.</p>
107
+
108
+ <h2>2. Technology Stack</h2>
109
+ <div class="tech-grid">
110
+ <div class="tech-box">
111
+ <h4>Core Framework</h4>
112
+ <ul>
113
+ <li><strong>Language:</strong> Python 3.10</li>
114
+ <li><strong>API Framework:</strong> FastAPI</li>
115
+ <li><strong>Server:</strong> Uvicorn (ASGI)</li>
116
+ </ul>
117
+ </div>
118
+ <div class="tech-box">
119
+ <h4>Data & Analytics</h4>
120
+ <ul>
121
+ <li><strong>Processing:</strong> Pandas, NumPy</li>
122
+ <li><strong>Forecasting:</strong> Statsmodels (ARIMA)</li>
123
+ <li><strong>Storage:</strong> Flat-file CSV (In-memory cache)</li>
124
+ </ul>
125
+ </div>
126
+ <div class="tech-box">
127
+ <h4>Frontend</h4>
128
+ <ul>
129
+ <li><strong>Structure:</strong> HTML5</li>
130
+ <li><strong>Styling:</strong> CSS3 (Custom Design System)</li>
131
+ <li><strong>Logic:</strong> Vanilla JavaScript (ES6+)</li>
132
+ </ul>
133
+ </div>
134
+ <div class="tech-box">
135
+ <h4>Deployment</h4>
136
+ <ul>
137
+ <li><strong>Containerization:</strong> Docker</li>
138
+ <li><strong>Platform:</strong> Hugging Face Spaces</li>
139
+ <li><strong>CI/CD:</strong> Git-based Workflow</li>
140
+ </ul>
141
+ </div>
142
+ </div>
143
+
144
+ <h2>3. Component Specifications</h2>
145
+ <table>
146
+ <thead>
147
+ <tr>
148
+ <th>Component</th>
149
+ <th>Specification</th>
150
+ <th>Function</th>
151
+ </tr>
152
+ </thead>
153
+ <tbody>
154
+ <tr>
155
+ <td><strong>Forecasting Engine</strong></td>
156
+ <td>ARIMA (1,1,1) Model</td>
157
+ <td>Generates 30-day demand projections based on historical time-series data.</td>
158
+ </tr>
159
+ <tr>
160
+ <td><strong>Intent Parser</strong></td>
161
+ <td>Regex / Pattern Matching</td>
162
+ <td>Identifies user intent (Forecast, Inventory, Supplier) with <50ms latency.</td>
163
+ </tr>
164
+ <tr>
165
+ <td><strong>LLM Integration</strong></td>
166
+ <td>TinyLlama 1.1B (GGUF)</td>
167
+ <td>Handles unstructured general queries and conversational context.</td>
168
+ </tr>
169
+ <tr>
170
+ <td><strong>PDF Generator</strong></td>
171
+ <td>FPDF Library</td>
172
+ <td>Dynamically compiles chat transcripts and data tables into downloadable reports.</td>
173
+ </tr>
174
+ </tbody>
175
+ </table>
176
+
177
+ <h2>4. Performance Metrics</h2>
178
+ <ul>
179
+ <li><strong>API Latency:</strong> &lt; 100ms (Standard Queries), &lt; 2s (Forecasting)</li>
180
+ <li><strong>Concurrent Sessions:</strong> Scalable via Docker replicas (Default: 2 vCPUs)</li>
181
+ <li><strong>Uptime:</strong> 99.9% (Hugging Face Infrastructure)</li>
182
+ </ul>
183
+
184
+ </body>
185
+
186
+ </html>
COMPLETE_PROCESS_FLOW.html ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Complete Process Flow - BMS AI Assistant</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: Arial, sans-serif;
17
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
18
+ padding: 20px;
19
+ }
20
+
21
+ .container {
22
+ max-width: 1200px;
23
+ margin: 0 auto;
24
+ background: white;
25
+ border-radius: 10px;
26
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
27
+ }
28
+
29
+ .header {
30
+ background: linear-gradient(135deg, #D01010 0%, #A00C0C 100%);
31
+ color: white;
32
+ padding: 30px;
33
+ text-align: center;
34
+ border-radius: 10px 10px 0 0;
35
+ }
36
+
37
+ .header h1 {
38
+ font-size: 2em;
39
+ margin-bottom: 10px;
40
+ }
41
+
42
+ .content {
43
+ padding: 30px;
44
+ }
45
+
46
+ .flow-step {
47
+ background: #f8f9fa;
48
+ border-left: 5px solid #D01010;
49
+ padding: 20px;
50
+ margin: 15px 0;
51
+ border-radius: 5px;
52
+ }
53
+
54
+ .flow-step h3 {
55
+ color: #D01010;
56
+ margin-bottom: 10px;
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 10px;
60
+ }
61
+
62
+ .step-num {
63
+ background: #D01010;
64
+ color: white;
65
+ width: 35px;
66
+ height: 35px;
67
+ border-radius: 50%;
68
+ display: inline-flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ font-weight: bold;
72
+ }
73
+
74
+ .arrow {
75
+ text-align: center;
76
+ font-size: 2em;
77
+ color: #D01010;
78
+ margin: 10px 0;
79
+ }
80
+
81
+ .code {
82
+ background: #2d2d2d;
83
+ color: #f8f8f2;
84
+ padding: 15px;
85
+ border-radius: 5px;
86
+ margin: 10px 0;
87
+ font-family: monospace;
88
+ font-size: 0.9em;
89
+ overflow-x: auto;
90
+ }
91
+
92
+ .branch {
93
+ display: grid;
94
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
95
+ gap: 10px;
96
+ margin: 15px 0;
97
+ }
98
+
99
+ .branch-item {
100
+ background: white;
101
+ border: 2px solid #D01010;
102
+ padding: 15px;
103
+ text-align: center;
104
+ border-radius: 5px;
105
+ }
106
+
107
+ .branch-item h4 {
108
+ color: #D01010;
109
+ margin-bottom: 5px;
110
+ }
111
+ </style>
112
+ </head>
113
+
114
+ <body>
115
+ <div class="container">
116
+ <div class="header">
117
+ <h1>🔄 Complete Technical Process Flow</h1>
118
+ <p>BMS AI Assistant - User to System to User</p>
119
+ </div>
120
+ <div class="content">
121
+
122
+ <div class="flow-step">
123
+ <h3><span class="step-num">1</span> User Input</h3>
124
+ <p><strong>Component:</strong> Frontend UI (index.html)</p>
125
+ <p><strong>Action:</strong> User types query in chat interface</p>
126
+ <p><strong>Example:</strong> "What is the forecast for BMS0015 next month?"</p>
127
+ <div class="code">userInput.addEventListener('keypress', (e) => {
128
+ if (e.key === 'Enter') sendMessage();
129
+ });</div>
130
+ </div>
131
+
132
+ <div class="arrow">↓</div>
133
+
134
+ <div class="flow-step">
135
+ <h3><span class="step-num">2</span> Client Processing</h3>
136
+ <p><strong>Component:</strong> JavaScript</p>
137
+ <p><strong>Action:</strong> Validate input, display user message, prepare API call</p>
138
+ <div class="code">const text = userInput.value.trim();
139
+ addMessage(text, 'user');</div>
140
+ </div>
141
+
142
+ <div class="arrow">↓</div>
143
+
144
+ <div class="flow-step">
145
+ <h3><span class="step-num">3</span> HTTP POST Request</h3>
146
+ <p><strong>Endpoint:</strong> /api/chat</p>
147
+ <p><strong>Method:</strong> POST</p>
148
+ <p><strong>Payload:</strong> {"message": "user query"}</p>
149
+ <div class="code">fetch('/api/chat', {
150
+ method: 'POST',
151
+ headers: {'Content-Type': 'application/json'},
152
+ body: JSON.stringify({message: text})
153
+ });</div>
154
+ </div>
155
+
156
+ <div class="arrow">↓</div>
157
+
158
+ <div class="flow-step">
159
+ <h3><span class="step-num">4</span> Server Receives Request</h3>
160
+ <p><strong>Component:</strong> FastAPI (main.py)</p>
161
+ <p><strong>Server:</strong> Uvicorn ASGI</p>
162
+ <div class="code">@app.post("/api/chat")
163
+ async def chat(request: ChatRequest):
164
+ message = request.message</div>
165
+ </div>
166
+
167
+ <div class="arrow">↓</div>
168
+
169
+ <div class="flow-step">
170
+ <h3><span class="step-num">5</span> Intent Parsing</h3>
171
+ <p><strong>Component:</strong> intent_parser.py</p>
172
+ <p><strong>Method:</strong> Regex pattern matching</p>
173
+ <p><strong>Extracts:</strong> Intent, Item code, Quantity, Location, Horizon</p>
174
+ <div class="code">parsed = parser.parse(message)
175
+ # Returns: {
176
+ # "intent": "demand_forecast",
177
+ # "item_code": "BMS0015",
178
+ # "horizon_days": 30
179
+ # }</div>
180
+ </div>
181
+
182
+ <div class="arrow">↓</div>
183
+
184
+ <div class="flow-step">
185
+ <h3><span class="step-num">6</span> Intent Routing</h3>
186
+ <p><strong>Decision Point:</strong> Route to appropriate handler</p>
187
+ <div class="branch">
188
+ <div class="branch-item">
189
+ <h4>Forecast</h4>
190
+ <p>forecasting.py</p>
191
+ </div>
192
+ <div class="branch-item">
193
+ <h4>Item Details</h4>
194
+ <p>data_loader.py</p>
195
+ </div>
196
+ <div class="branch-item">
197
+ <h4>Inventory</h4>
198
+ <p>data_loader.py</p>
199
+ </div>
200
+ <div class="branch-item">
201
+ <h4>Supplier</h4>
202
+ <p>data_loader.py</p>
203
+ </div>
204
+ <div class="branch-item">
205
+ <h4>Requisition</h4>
206
+ <p>data_loader.py</p>
207
+ </div>
208
+ <div class="branch-item">
209
+ <h4>Status</h4>
210
+ <p>data_loader.py</p>
211
+ </div>
212
+ <div class="branch-item">
213
+ <h4>PDF</h4>
214
+ <p>pdf_generator.py</p>
215
+ </div>
216
+ <div class="branch-item">
217
+ <h4>Chat</h4>
218
+ <p>llm_engine.py</p>
219
+ </div>
220
+ </div>
221
+ </div>
222
+
223
+ <div class="arrow">↓</div>
224
+
225
+ <div class="flow-step">
226
+ <h3><span class="step-num">7</span> Business Logic Execution</h3>
227
+ <p><strong>Example:</strong> Demand Forecast</p>
228
+ <p>1. Load demand_history.csv<br>
229
+ 2. Filter by item_code<br>
230
+ 3. Fit ARIMA model<br>
231
+ 4. Generate forecast<br>
232
+ 5. Format as JSON</p>
233
+ <div class="code">forecast_data = forecast_demand(item_code, horizon)
234
+ # Returns: [{"date": "2025-01-01", "qty": 150}, ...]</div>
235
+ </div>
236
+
237
+ <div class="arrow">↓</div>
238
+
239
+ <div class="flow-step">
240
+ <h3><span class="step-num">8</span> Data Retrieval</h3>
241
+ <p><strong>Component:</strong> data_loader.py</p>
242
+ <p><strong>Sources:</strong> items.csv, inventory.csv, suppliers.csv, etc.</p>
243
+ <p><strong>Technology:</strong> Pandas DataFrames</p>
244
+ <div class="code">item = loader.items_df[loader.items_df['item_code'] == 'BMS0015']</div>
245
+ </div>
246
+
247
+ <div class="arrow">↓</div>
248
+
249
+ <div class="flow-step">
250
+ <h3><span class="step-num">9</span> LLM Processing (if needed)</h3>
251
+ <p><strong>Component:</strong> llm_engine.py</p>
252
+ <p><strong>Trigger:</strong> Only for general_chat intent</p>
253
+ <p><strong>Model:</strong> TinyLlama 1.1B</p>
254
+ <p><strong>Time:</strong> 5-15 seconds</p>
255
+ <div class="code">response = llm.generate_response(query)
256
+ # Uses company_context.txt for RAG</div>
257
+ </div>
258
+
259
+ <div class="arrow">↓</div>
260
+
261
+ <div class="flow-step">
262
+ <h3><span class="step-num">10</span> Response Formatting</h3>
263
+ <p><strong>Component:</strong> main.py</p>
264
+ <p><strong>Format:</strong> JSON</p>
265
+ <div class="code">return {
266
+ "intent": "demand_forecast",
267
+ "answer": "Forecast for BMS0015...",
268
+ "forecast": [{"date": "2025-01-01", "qty": 150}]
269
+ }</div>
270
+ </div>
271
+
272
+ <div class="arrow">↓</div>
273
+
274
+ <div class="flow-step">
275
+ <h3><span class="step-num">11</span> HTTP Response</h3>
276
+ <p><strong>Status:</strong> 200 OK</p>
277
+ <p><strong>Content-Type:</strong> application/json</p>
278
+ <p><strong>Sent to:</strong> Client browser</p>
279
+ </div>
280
+
281
+ <div class="arrow">↓</div>
282
+
283
+ <div class="flow-step">
284
+ <h3><span class="step-num">12</span> Client Receives Response</h3>
285
+ <p><strong>Component:</strong> JavaScript Fetch API</p>
286
+ <p><strong>Action:</strong> Parse JSON response</p>
287
+ <div class="code">const data = await response.json();
288
+ let botHtml = data.answer;</div>
289
+ </div>
290
+
291
+ <div class="arrow">↓</div>
292
+
293
+ <div class="flow-step">
294
+ <h3><span class="step-num">13</span> UI Rendering</h3>
295
+ <p><strong>Component:</strong> JavaScript DOM manipulation</p>
296
+ <p><strong>Actions:</strong></p>
297
+ <p>1. Create message bubble<br>
298
+ 2. Add bot icon<br>
299
+ 3. Render forecast table (if present)<br>
300
+ 4. Add download button (if PDF)<br>
301
+ 5. Scroll to bottom</p>
302
+ <div class="code">addMessage(botHtml, 'bot', true);
303
+ if (data.forecast) {
304
+ botHtml += renderForecastTable(data.forecast);
305
+ }</div>
306
+ </div>
307
+
308
+ <div class="arrow">↓</div>
309
+
310
+ <div class="flow-step">
311
+ <h3><span class="step-num">14</span> User Sees Response</h3>
312
+ <p><strong>Display:</strong> Chat bubble with bot icon</p>
313
+ <p><strong>Features:</strong> Formatted text, tables, download links</p>
314
+ <p><strong>Animation:</strong> Fade-in effect</p>
315
+ <p><strong>Interaction:</strong> User can copy text, download PDFs, ask follow-up</p>
316
+ </div>
317
+
318
+ <div style="background: #e8f5e9; padding: 20px; border-radius: 5px; margin-top: 30px;">
319
+ <h3 style="color: #2e7d32; margin-bottom: 10px;">✅ Flow Complete</h3>
320
+ <p><strong>Total Steps:</strong> 14</p>
321
+ <p><strong>Average Time:</strong> 2-15 seconds (depending on intent)</p>
322
+ <p><strong>Technologies Used:</strong> HTML/JS, FastAPI, Pandas, ARIMA, TinyLlama</p>
323
+ </div>
324
+
325
+ </div>
326
+ </div>
327
+ </body>
328
+
329
+ </html>
Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y gcc g++ && rm -rf /var/lib/apt/lists/*
6
+
7
+ COPY requirements.txt .
8
+ RUN pip install --no-cache-dir -r requirements.txt
9
+
10
+ COPY app/ ./app/
11
+ COPY data/ ./data/
12
+ COPY static/ ./static/
13
+
14
+ RUN mkdir -p static/reports
15
+
16
+ EXPOSE 7860
17
+ ENV PYTHONUNBUFFERED=1
18
+
19
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: BMS AI Assistant
3
+ emoji: 🤖
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: docker
7
+ pinned: false
8
+ app_port: 7860
9
+ ---
10
+
11
+ # BMS AI Assistant
12
+
13
+ An intelligent chatbot for Business Management Systems.
14
+
15
+ ## Features
16
+ - Demand Forecasting
17
+ - Inventory Management
18
+ - Supplier Information
19
+ - PDF Reporting
STANDALONE_DEMO.html ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>BMS AI Assistant - Standalone Demo</title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ :root {
11
+ --primary-color: #D01010;
12
+ --primary-dark: #A00C0C;
13
+ --bg-color: #F3F4F6;
14
+ --chat-bg: #FFFFFF;
15
+ --user-msg-bg: #D01010;
16
+ --user-msg-text: #FFFFFF;
17
+ --bot-msg-bg: #F3F4F6;
18
+ --bot-msg-text: #1F2937;
19
+ --border-color: #E5E7EB;
20
+ --text-color: #1F2937;
21
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
22
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
23
+ }
24
+
25
+ body {
26
+ font-family: 'Inter', sans-serif;
27
+ background-color: var(--bg-color);
28
+ margin: 0;
29
+ display: flex;
30
+ justify-content: center;
31
+ height: 100vh;
32
+ color: var(--text-color);
33
+ }
34
+
35
+ .chat-container {
36
+ width: 100%;
37
+ max-width: 900px;
38
+ background-color: var(--chat-bg);
39
+ box-shadow: var(--shadow-md);
40
+ display: flex;
41
+ flex-direction: column;
42
+ height: 100%;
43
+ overflow: hidden;
44
+ }
45
+
46
+ .header {
47
+ background-color: var(--primary-color);
48
+ color: white;
49
+ padding: 20px;
50
+ text-align: center;
51
+ font-size: 1.25rem;
52
+ font-weight: 600;
53
+ box-shadow: var(--shadow-sm);
54
+ z-index: 10;
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ gap: 10px;
59
+ }
60
+
61
+ .header svg {
62
+ width: 24px;
63
+ height: 24px;
64
+ fill: currentColor;
65
+ }
66
+
67
+ .demo-badge {
68
+ background: #FFA500;
69
+ padding: 5px 15px;
70
+ border-radius: 20px;
71
+ font-size: 0.8em;
72
+ margin-left: 10px;
73
+ }
74
+
75
+ .chat-window {
76
+ flex: 1;
77
+ padding: 20px;
78
+ overflow-y: auto;
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 16px;
82
+ scroll-behavior: smooth;
83
+ }
84
+
85
+ .message {
86
+ max-width: 80%;
87
+ padding: 14px 18px;
88
+ border-radius: 16px;
89
+ line-height: 1.5;
90
+ position: relative;
91
+ animation: fadeIn 0.3s ease-out;
92
+ display: flex;
93
+ align-items: flex-start;
94
+ gap: 12px;
95
+ font-size: 0.95rem;
96
+ box-shadow: var(--shadow-sm);
97
+ }
98
+
99
+ @keyframes fadeIn {
100
+ from {
101
+ opacity: 0;
102
+ transform: translateY(10px);
103
+ }
104
+
105
+ to {
106
+ opacity: 1;
107
+ transform: translateY(0);
108
+ }
109
+ }
110
+
111
+ .message.user {
112
+ align-self: flex-end;
113
+ background-color: var(--user-msg-bg);
114
+ color: var(--user-msg-text);
115
+ border-bottom-right-radius: 4px;
116
+ flex-direction: row-reverse;
117
+ }
118
+
119
+ .message.bot {
120
+ align-self: flex-start;
121
+ background-color: var(--bot-msg-bg);
122
+ color: var(--bot-msg-text);
123
+ border-bottom-left-radius: 4px;
124
+ border: 1px solid var(--border-color);
125
+ }
126
+
127
+ .icon-container {
128
+ width: 36px;
129
+ height: 36px;
130
+ border-radius: 50%;
131
+ display: flex;
132
+ align-items: center;
133
+ justify-content: center;
134
+ flex-shrink: 0;
135
+ background-color: white;
136
+ box-shadow: var(--shadow-sm);
137
+ }
138
+
139
+ .message.user .icon-container {
140
+ color: var(--primary-color);
141
+ }
142
+
143
+ .message.bot .icon-container {
144
+ background-color: var(--primary-color);
145
+ color: white;
146
+ }
147
+
148
+ .icon-container svg {
149
+ width: 20px;
150
+ height: 20px;
151
+ fill: currentColor;
152
+ }
153
+
154
+ .message-content {
155
+ flex: 1;
156
+ word-wrap: break-word;
157
+ }
158
+
159
+ .input-area {
160
+ padding: 20px;
161
+ border-top: 1px solid var(--border-color);
162
+ display: flex;
163
+ gap: 12px;
164
+ background-color: #fff;
165
+ align-items: center;
166
+ }
167
+
168
+ input[type="text"] {
169
+ flex: 1;
170
+ padding: 14px 20px;
171
+ border: 1px solid var(--border-color);
172
+ border-radius: 30px;
173
+ outline: none;
174
+ font-size: 1rem;
175
+ transition: all 0.2s;
176
+ font-family: inherit;
177
+ background-color: #F9FAFB;
178
+ }
179
+
180
+ input[type="text"]:focus {
181
+ border-color: var(--primary-color);
182
+ background-color: #fff;
183
+ box-shadow: 0 0 0 3px rgba(208, 16, 16, 0.1);
184
+ }
185
+
186
+ button {
187
+ background-color: var(--primary-color);
188
+ color: white;
189
+ border: none;
190
+ padding: 14px 28px;
191
+ border-radius: 30px;
192
+ cursor: pointer;
193
+ font-weight: 600;
194
+ transition: background-color 0.2s, transform 0.1s;
195
+ font-family: inherit;
196
+ display: flex;
197
+ align-items: center;
198
+ gap: 8px;
199
+ }
200
+
201
+ button:hover {
202
+ background-color: var(--primary-dark);
203
+ }
204
+
205
+ button:active {
206
+ transform: scale(0.98);
207
+ }
208
+
209
+ .chart-container {
210
+ margin-top: 12px;
211
+ background: #fff;
212
+ border-radius: 8px;
213
+ border: 1px solid var(--border-color);
214
+ overflow: hidden;
215
+ }
216
+
217
+ table {
218
+ width: 100%;
219
+ border-collapse: collapse;
220
+ font-size: 0.9rem;
221
+ }
222
+
223
+ th,
224
+ td {
225
+ padding: 10px 14px;
226
+ text-align: left;
227
+ border-bottom: 1px solid var(--border-color);
228
+ }
229
+
230
+ th {
231
+ background-color: #F9FAFB;
232
+ font-weight: 600;
233
+ color: var(--text-color);
234
+ }
235
+
236
+ tr:last-child td {
237
+ border-bottom: none;
238
+ }
239
+
240
+ .download-btn {
241
+ display: inline-flex;
242
+ align-items: center;
243
+ gap: 8px;
244
+ background-color: var(--primary-color);
245
+ color: white;
246
+ padding: 10px 20px;
247
+ text-decoration: none;
248
+ border-radius: 25px;
249
+ font-weight: 600;
250
+ margin-top: 12px;
251
+ transition: background-color 0.2s;
252
+ font-size: 0.9rem;
253
+ cursor: pointer;
254
+ }
255
+
256
+ .download-btn:hover {
257
+ background-color: var(--primary-dark);
258
+ }
259
+ </style>
260
+ </head>
261
+
262
+ <body>
263
+
264
+ <div class="chat-container">
265
+ <div class="header">
266
+ <svg viewBox="0 0 24 24">
267
+ <path
268
+ d="M12 2L2 7l10 5 10-5-10-5zm0 9l2.5-1.25L12 8.5l-2.5 1.25L12 11zm0 2.5l-5-2.5-5 2.5L12 22l10-8.5-5-2.5-5 2.5z" />
269
+ </svg>
270
+ BMS AI Assistant
271
+ <span class="demo-badge">DEMO MODE</span>
272
+ </div>
273
+ <div class="chat-window" id="chat-window">
274
+ <div class="message bot">
275
+ <div class="icon-container">
276
+ <svg viewBox="0 0 24 24">
277
+ <path
278
+ d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a7 7 0 0 1 7 7h1a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v1a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1H2a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h1a7 7 0 0 1 7-7V5.73C9.4 5.39 9 4.74 9 4a2 2 0 0 1 2-2M7.5 13A2.5 2.5 0 0 0 5 15.5A2.5 2.5 0 0 0 7.5 18A2.5 2.5 0 0 0 10 15.5A2.5 2.5 0 0 0 7.5 13m9 0a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5a2.5 2.5 0 0 0-2.5-2.5M12 8a6 6 0 0 0-6 6h12a6 6 0 0 0-6-6z" />
279
+ </svg>
280
+ </div>
281
+ <div class="message-content">
282
+ <strong>Hello! I am your BMS AI Assistant.</strong><br><br>
283
+ I can help you with:<br>
284
+ • Demand Forecasting (e.g., "Forecast for BMS0015")<br>
285
+ • Inventory Checks (e.g., "Stock for BMS0015")<br>
286
+ • Supplier Information<br>
287
+ • Order Requisitions<br>
288
+ • General Inquiries<br><br>
289
+ How can I assist you today?
290
+ </div>
291
+ </div>
292
+ </div>
293
+ <div class="input-area">
294
+ <input type="text" id="user-input" placeholder="Type your query..." autocomplete="off">
295
+ <button onclick="sendMessage()">
296
+ Send
297
+ <svg viewBox="0 0 24 24" style="width: 16px; height: 16px;">
298
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
299
+ </svg>
300
+ </button>
301
+ </div>
302
+ </div>
303
+
304
+ <script>
305
+ const chatWindow = document.getElementById('chat-window');
306
+ const userInput = document.getElementById('user-input');
307
+
308
+ // Mock data
309
+ const mockData = {
310
+ forecast_bms0015: [
311
+ { date: '2025-12-01', qty: 152 },
312
+ { date: '2025-12-02', qty: 148 },
313
+ { date: '2025-12-03', qty: 155 },
314
+ { date: '2025-12-04', qty: 151 },
315
+ { date: '2025-12-05', qty: 149 },
316
+ { date: '2025-12-06', qty: 153 },
317
+ { date: '2025-12-07', qty: 150 }
318
+ ],
319
+ forecast_bms0020: [
320
+ { date: '2025-12-01', qty: 89 },
321
+ { date: '2025-12-02', qty: 92 },
322
+ { date: '2025-12-03', qty: 87 },
323
+ { date: '2025-12-04', qty: 90 },
324
+ { date: '2025-12-05', qty: 88 },
325
+ { date: '2025-12-06', qty: 91 },
326
+ { date: '2025-12-07', qty: 89 }
327
+ ]
328
+ };
329
+
330
+ userInput.addEventListener('keypress', function (e) {
331
+ if (e.key === 'Enter') {
332
+ sendMessage();
333
+ }
334
+ });
335
+
336
+ function sendMessage() {
337
+ const text = userInput.value.trim();
338
+ if (!text) return;
339
+
340
+ addMessage(text, 'user');
341
+ userInput.value = '';
342
+
343
+ // Simulate processing delay
344
+ setTimeout(() => {
345
+ const response = processQuery(text.toLowerCase());
346
+ addMessage(response.answer, 'bot', true, response.forecast);
347
+ }, 500);
348
+ }
349
+
350
+ function processQuery(query) {
351
+ // Greeting
352
+ if (query.match(/^(hi|hello|hey|greetings)$/)) {
353
+ return {
354
+ answer: "Hello! I am the BMS AI Assistant. How can I help you today?"
355
+ };
356
+ }
357
+
358
+ // Forecast
359
+ if (query.includes('forecast') || query.includes('predict')) {
360
+ if (query.includes('bms0015')) {
361
+ return {
362
+ answer: "📊 <strong>Demand Forecast for BMS0015</strong> (Next 30 days)<br><br>Based on historical data analysis using ARIMA model:",
363
+ forecast: mockData.forecast_bms0015
364
+ };
365
+ } else if (query.includes('bms0020')) {
366
+ return {
367
+ answer: "📊 <strong>Demand Forecast for BMS0020</strong> (Next 30 days)<br><br>Based on historical data analysis using ARIMA model:",
368
+ forecast: mockData.forecast_bms0020
369
+ };
370
+ } else {
371
+ return { answer: "Please specify an item code (e.g., BMS0015 or BMS0020)." };
372
+ }
373
+ }
374
+
375
+ // Item details
376
+ if (query.includes('details') || query.includes('about') || query.includes('info')) {
377
+ if (query.includes('bms0015')) {
378
+ return { answer: "<strong>Item Details:</strong><br>• Code: BMS0015<br>• Description: Fuel Water Separator<br>• UOM: Each<br>• List Price: $45.99" };
379
+ } else if (query.includes('bms0020')) {
380
+ return { answer: "<strong>Item Details:</strong><br>• Code: BMS0020<br>• Description: Lube Oil Filter<br>• UOM: Each<br>• List Price: $32.50" };
381
+ }
382
+ }
383
+
384
+ // Inventory
385
+ if (query.includes('inventory') || query.includes('stock') || query.includes('available')) {
386
+ if (query.includes('bms0015')) {
387
+ return { answer: "📦 <strong>Inventory Status for BMS0015:</strong><br>• On Hand: 450 units<br>• Location: NA Warehouse<br>• Last Updated: 2025-11-29" };
388
+ } else if (query.includes('bms0020')) {
389
+ return { answer: "📦 <strong>Inventory Status for BMS0020:</strong><br>• On Hand: 320 units<br>• Location: NA Warehouse<br>• Last Updated: 2025-11-29" };
390
+ }
391
+ }
392
+
393
+ // Supplier
394
+ if (query.includes('supplier') || query.includes('vendor') || query.includes('who supplies')) {
395
+ return { answer: "🏭 <strong>Supplier Information:</strong><br>• Supplier: Acme Filters Inc.<br>• Lead Time: 14 days<br>• Contact: [email protected]" };
396
+ }
397
+
398
+ // Order/Requisition
399
+ if (query.includes('order') || query.includes('buy') || query.includes('purchase') || query.includes('requisition')) {
400
+ const reqId = 'REQ' + Math.floor(Math.random() * 9000 + 1000);
401
+ return { answer: `✅ <strong>Requisition Created Successfully!</strong><br>• Requisition ID: ${reqId}<br>• Item: BMS0015<br>• Quantity: 50<br>• Status: Pending` };
402
+ }
403
+
404
+ // System status
405
+ if (query.includes('status') || query.includes('alerts') || query.includes('dashboard')) {
406
+ return { answer: "⚠️ <strong>System Alerts:</strong><br>• BMS0015: Low stock warning (below reorder point)<br>• AB9999: Supplier lead time increased<br><br>✅ All other systems normal." };
407
+ }
408
+
409
+ // PDF
410
+ if (query.includes('pdf') || query.includes('download') || query.includes('report')) {
411
+ return { answer: '📄 <strong>Report Generation</strong><br><br><a href="#" class="download-btn" onclick="alert(\'In production, this would download a PDF report.\'); return false;">📥 Download Forecast Report</a>' };
412
+ }
413
+
414
+ // General about BMS
415
+ if (query.includes('bms') || query.includes('what do') || query.includes('company')) {
416
+ return { answer: "BMS Inc. is a global power leader that designs, manufactures, distributes, and services a broad portfolio of power solutions. Our products range from diesel, natural gas, electric and hybrid powertrains to components including filtration, aftertreatment, and turbochargers." };
417
+ }
418
+
419
+ // Default
420
+ return { answer: "I can help you with demand forecasting, inventory checks, supplier information, and order requisitions. Try asking about item BMS0015 or BMS0020!" };
421
+ }
422
+
423
+ function addMessage(text, sender, isHtml = false, forecast = null) {
424
+ const div = document.createElement('div');
425
+ div.className = `message ${sender}`;
426
+
427
+ const iconDiv = document.createElement('div');
428
+ iconDiv.className = 'icon-container';
429
+
430
+ if (sender === 'user') {
431
+ iconDiv.innerHTML = `<svg viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>`;
432
+ } else {
433
+ iconDiv.innerHTML = `<svg viewBox="0 0 24 24"><path d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a7 7 0 0 1 7 7h1a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v1a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1H2a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h1a7 7 0 0 1 7-7V5.73C9.4 5.39 9 4.74 9 4a2 2 0 0 1 2-2M7.5 13A2.5 2.5 0 0 0 5 15.5A2.5 2.5 0 0 0 7.5 18A2.5 2.5 0 0 0 10 15.5A2.5 2.5 0 0 0 7.5 13m9 0a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5a2.5 2.5 0 0 0-2.5-2.5M12 8a6 6 0 0 0-6 6h12a6 6 0 0 0-6-6z"/></svg>`;
434
+ }
435
+
436
+ const contentDiv = document.createElement('div');
437
+ contentDiv.className = 'message-content';
438
+
439
+ if (isHtml) {
440
+ contentDiv.innerHTML = text;
441
+ if (forecast) {
442
+ contentDiv.innerHTML += renderForecastTable(forecast);
443
+ }
444
+ } else {
445
+ contentDiv.textContent = text;
446
+ }
447
+
448
+ div.appendChild(iconDiv);
449
+ div.appendChild(contentDiv);
450
+
451
+ chatWindow.appendChild(div);
452
+ chatWindow.scrollTop = chatWindow.scrollHeight;
453
+ }
454
+
455
+ function renderForecastTable(forecastData) {
456
+ let html = '<div class="chart-container"><table><thead><tr><th>Date</th><th>Quantity</th></tr></thead><tbody>';
457
+
458
+ forecastData.forEach(row => {
459
+ html += `<tr><td>${row.date}</td><td>${row.qty}</td></tr>`;
460
+ });
461
+
462
+ html += '</tbody></table></div>';
463
+ return html;
464
+ }
465
+ </script>
466
+ </body>
467
+
468
+ </html>
TEAM_ANNOUNCEMENT_EMAIL.md ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Team Announcement Email Drafts
2
+
3
+ Here are three versions of the email you can send to your team. Choose the one that best fits your team's culture.
4
+
5
+ ---
6
+
7
+ ## Option 1: The "Vibe Coding" Approach (Recommended)
8
+ *Best for highlighting the innovative/experimental nature of the project.*
9
+
10
+ **Subject:** POC: BMS AI Assistant - Built with Gemini & Open Source Tools
11
+
12
+ Hi Team,
13
+
14
+ I did some "vibe coding" this weekend using Gemini to build a Proof of Concept (POC) for our supply chain operations. I wanted to see how quickly we could spin up a functional AI assistant using 100% open-source tools without any licensing costs.
15
+
16
+ **What it is:**
17
+ A chatbot that can forecast demand, check inventory, and answer supplier questions in real-time.
18
+
19
+ **The Tech Stack:**
20
+ It’s running entirely on open-source technology:
21
+ * **Backend:** FastAPI (Python)
22
+ * **AI Model:** TinyLlama (Local inference, no API costs)
23
+ * **Forecasting:** ARIMA (Statistical modeling)
24
+ * **Hosting:** Hugging Face Spaces (Dockerized)
25
+
26
+ **Try it out here:**
27
+ 👉 **[Link to BMS AI Assistant](https://huggingface.co/spaces/bmsuser/BMS-AI-BOT)**
28
+ *(Note: It might take a minute to wake up if it's been idle)*
29
+
30
+ **Documentation:**
31
+ I've attached the **Technical Specification** and **Process Flow** documents if you want to dive into the architecture.
32
+
33
+ Please play around with it and let me know what you think! Since this is a POC, I'm looking for suggestions on features or use cases we might have missed.
34
+
35
+ Best,
36
+
37
+ [Your Name]
38
+
39
+ ---
40
+
41
+ ## Option 2: Professional & Direct
42
+ *Best for a formal corporate environment.*
43
+
44
+ **Subject:** For Review: BMS AI Assistant Prototype (Open Source POC)
45
+
46
+ Team,
47
+
48
+ I have deployed a prototype of the **BMS AI Assistant**, a conversational tool designed to streamline our access to demand and inventory data.
49
+
50
+ This POC was built to demonstrate the feasibility of using purely open-source, cost-effective technologies for enterprise AI. It leverages local LLMs and statistical forecasting models to provide instant answers without external API dependencies.
51
+
52
+ **Access the Application:**
53
+ https://huggingface.co/spaces/bmsuser/BMS-AI-BOT
54
+
55
+ **Key Features to Test:**
56
+ * **Demand Forecasting:** Ask for forecasts on specific items (e.g., "Forecast for BMS0015").
57
+ * **Inventory Checks:** Query real-time stock levels.
58
+ * **Supplier Info:** Retrieve vendor details.
59
+
60
+ I have attached the full **Technical Specifications** for your review. Please test the system and provide your feedback on its potential utility for our workflows.
61
+
62
+ Regards,
63
+
64
+ [Your Name]
65
+
66
+ ---
67
+
68
+ ## Option 3: Short & Casual
69
+ *Best for Slack/Teams or quick updates.*
70
+
71
+ **Subject:** Check this out: AI Bot for BMS (POC)
72
+
73
+ Hey everyone,
74
+
75
+ I hacked together a quick POC for an AI Assistant using Gemini and some open-source tools (FastAPI, Llama, etc.). It’s actually working pretty well for forecasting and inventory checks.
76
+
77
+ **Live Demo:** https://huggingface.co/spaces/bmsuser/BMS-AI-BOT
78
+
79
+ I've attached the specs if you're curious about how it works under the hood.
80
+
81
+ Give it a spin and let me know if this vibe works for us!
82
+
83
+ Cheers,
84
+
85
+ [Your Name]
TECHNICAL_SPECIFICATION.html ADDED
@@ -0,0 +1,603 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>BMS AI Assistant - Technical Specification</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ line-height: 1.6;
18
+ color: #333;
19
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
20
+ padding: 20px;
21
+ }
22
+
23
+ .container {
24
+ max-width: 1200px;
25
+ margin: 0 auto;
26
+ background: white;
27
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
28
+ border-radius: 10px;
29
+ overflow: hidden;
30
+ }
31
+
32
+ .header {
33
+ background: linear-gradient(135deg, #D01010 0%, #A00C0C 100%);
34
+ color: white;
35
+ padding: 40px;
36
+ text-align: center;
37
+ }
38
+
39
+ .header h1 {
40
+ font-size: 2.5em;
41
+ margin-bottom: 10px;
42
+ }
43
+
44
+ .header p {
45
+ font-size: 1.2em;
46
+ opacity: 0.9;
47
+ }
48
+
49
+ .content {
50
+ padding: 40px;
51
+ }
52
+
53
+ .section {
54
+ margin-bottom: 40px;
55
+ }
56
+
57
+ .section h2 {
58
+ color: #D01010;
59
+ font-size: 2em;
60
+ margin-bottom: 20px;
61
+ padding-bottom: 10px;
62
+ border-bottom: 3px solid #D01010;
63
+ }
64
+
65
+ .section h3 {
66
+ color: #333;
67
+ font-size: 1.5em;
68
+ margin: 20px 0 10px 0;
69
+ }
70
+
71
+ .tech-stack {
72
+ display: grid;
73
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
74
+ gap: 20px;
75
+ margin: 20px 0;
76
+ }
77
+
78
+ .tech-card {
79
+ background: #f8f9fa;
80
+ padding: 20px;
81
+ border-radius: 8px;
82
+ border-left: 4px solid #D01010;
83
+ transition: transform 0.3s;
84
+ }
85
+
86
+ .tech-card:hover {
87
+ transform: translateY(-5px);
88
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
89
+ }
90
+
91
+ .tech-card h4 {
92
+ color: #D01010;
93
+ margin-bottom: 10px;
94
+ }
95
+
96
+ .tech-card ul {
97
+ list-style: none;
98
+ padding-left: 0;
99
+ }
100
+
101
+ .tech-card li:before {
102
+ content: "✓ ";
103
+ color: #D01010;
104
+ font-weight: bold;
105
+ }
106
+
107
+ .flow-diagram {
108
+ background: #f8f9fa;
109
+ padding: 30px;
110
+ border-radius: 8px;
111
+ margin: 20px 0;
112
+ }
113
+
114
+ .flow-step {
115
+ background: white;
116
+ padding: 20px;
117
+ margin: 15px 0;
118
+ border-radius: 8px;
119
+ border-left: 4px solid #D01010;
120
+ position: relative;
121
+ }
122
+
123
+ .flow-step:before {
124
+ content: attr(data-step);
125
+ position: absolute;
126
+ left: -15px;
127
+ top: 50%;
128
+ transform: translateY(-50%);
129
+ background: #D01010;
130
+ color: white;
131
+ width: 30px;
132
+ height: 30px;
133
+ border-radius: 50%;
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: center;
137
+ font-weight: bold;
138
+ }
139
+
140
+ .flow-step h4 {
141
+ color: #D01010;
142
+ margin-bottom: 10px;
143
+ }
144
+
145
+ .code-block {
146
+ background: #2d2d2d;
147
+ color: #f8f8f2;
148
+ padding: 20px;
149
+ border-radius: 8px;
150
+ overflow-x: auto;
151
+ margin: 15px 0;
152
+ font-family: 'Courier New', monospace;
153
+ }
154
+
155
+ .specs-table {
156
+ width: 100%;
157
+ border-collapse: collapse;
158
+ margin: 20px 0;
159
+ }
160
+
161
+ .specs-table th,
162
+ .specs-table td {
163
+ padding: 15px;
164
+ text-align: left;
165
+ border-bottom: 1px solid #ddd;
166
+ }
167
+
168
+ .specs-table th {
169
+ background: #D01010;
170
+ color: white;
171
+ font-weight: bold;
172
+ }
173
+
174
+ .specs-table tr:hover {
175
+ background: #f8f9fa;
176
+ }
177
+
178
+ .badge {
179
+ display: inline-block;
180
+ padding: 5px 10px;
181
+ border-radius: 20px;
182
+ font-size: 0.85em;
183
+ font-weight: bold;
184
+ margin: 5px;
185
+ }
186
+
187
+ .badge-primary {
188
+ background: #D01010;
189
+ color: white;
190
+ }
191
+
192
+ .badge-success {
193
+ background: #28a745;
194
+ color: white;
195
+ }
196
+
197
+ .badge-info {
198
+ background: #17a2b8;
199
+ color: white;
200
+ }
201
+
202
+ .architecture-diagram {
203
+ background: white;
204
+ padding: 30px;
205
+ border-radius: 8px;
206
+ text-align: center;
207
+ margin: 20px 0;
208
+ }
209
+
210
+ .layer {
211
+ background: #f8f9fa;
212
+ padding: 20px;
213
+ margin: 10px 0;
214
+ border-radius: 8px;
215
+ border: 2px solid #D01010;
216
+ }
217
+
218
+ .layer h4 {
219
+ color: #D01010;
220
+ margin-bottom: 10px;
221
+ }
222
+
223
+ .component {
224
+ display: inline-block;
225
+ background: white;
226
+ padding: 10px 20px;
227
+ margin: 5px;
228
+ border-radius: 5px;
229
+ border: 1px solid #ddd;
230
+ }
231
+
232
+ @media print {
233
+ body {
234
+ background: white;
235
+ }
236
+
237
+ .container {
238
+ box-shadow: none;
239
+ }
240
+ }
241
+ </style>
242
+ </head>
243
+
244
+ <body>
245
+ <div class="container">
246
+ <div class="header">
247
+ <h1>🏭 BMS AI Assistant</h1>
248
+ <p>Technical Specification & Process Flow Documentation</p>
249
+ <p style="font-size: 0.9em; margin-top: 10px;">Version 1.0 | Enterprise Demand Forecasting System</p>
250
+ </div>
251
+
252
+ <div class="content">
253
+ <!-- Executive Summary -->
254
+ <div class="section">
255
+ <h2>📋 Executive Summary</h2>
256
+ <p>The BMS AI Assistant is an enterprise-grade, zero-cost chatbot system designed for demand
257
+ forecasting, inventory management, and ERP integration. Built with open-source technologies, it
258
+ combines rule-based intent parsing with local AI capabilities to provide intelligent, context-aware
259
+ responses without external API dependencies.</p>
260
+
261
+ <div style="margin-top: 20px;">
262
+ <span class="badge badge-primary">Zero Cost</span>
263
+ <span class="badge badge-success">100% Open Source</span>
264
+ <span class="badge badge-info">Local AI</span>
265
+ <span class="badge badge-primary">Enterprise Ready</span>
266
+ </div>
267
+ </div>
268
+
269
+ <!-- System Architecture -->
270
+ <div class="section">
271
+ <h2>🏗️ System Architecture</h2>
272
+
273
+ <div class="architecture-diagram">
274
+ <div class="layer">
275
+ <h4>Presentation Layer</h4>
276
+ <div class="component">HTML5 + CSS3</div>
277
+ <div class="component">JavaScript (Vanilla)</div>
278
+ <div class="component">SVG Icons</div>
279
+ <div class="component">Hugging Face Spaces</div>
280
+ </div>
281
+
282
+ <div class="layer">
283
+ <h4>API Layer</h4>
284
+ <div class="component">FastAPI</div>
285
+ <div class="component">Uvicorn (ASGI)</div>
286
+ <div class="component">REST Endpoints</div>
287
+ </div>
288
+
289
+ <div class="layer">
290
+ <h4>Business Logic Layer</h4>
291
+ <div class="component">Intent Parser</div>
292
+ <div class="component">ARIMA Forecasting</div>
293
+ <div class="component">LLM Engine</div>
294
+ <div class="component">PDF Generator</div>
295
+ </div>
296
+
297
+ <div class="layer">
298
+ <h4>Data Layer</h4>
299
+ <div class="component">CSV Files</div>
300
+ <div class="component">Pandas DataFrames</div>
301
+ <div class="component">In-Memory Cache</div>
302
+ </div>
303
+
304
+ <div class="layer">
305
+ <h4>AI/ML Layer</h4>
306
+ <div class="component">TinyLlama 1.1B</div>
307
+ <div class="component">ctransformers</div>
308
+ <div class="component">Statsmodels (ARIMA)</div>
309
+ </div>
310
+ </div>
311
+ </div>
312
+
313
+ <!-- Technology Stack -->
314
+ <div class="section">
315
+ <h2>💻 Technology Stack</h2>
316
+
317
+ <div class="tech-stack">
318
+ <div class="tech-card">
319
+ <h4>Backend Framework</h4>
320
+ <ul>
321
+ <li>FastAPI 0.104+</li>
322
+ <li>Uvicorn (ASGI Server)</li>
323
+ <li>Python 3.10+</li>
324
+ <li>Async/Await Support</li>
325
+ </ul>
326
+ </div>
327
+
328
+ <div class="tech-card">
329
+ <h4>AI/ML Stack</h4>
330
+ <ul>
331
+ <li>TinyLlama 1.1B (GGUF)</li>
332
+ <li>ctransformers 0.2.27+</li>
333
+ <li>Hugging Face Hub</li>
334
+ <li>Statsmodels (ARIMA)</li>
335
+ </ul>
336
+ </div>
337
+
338
+ <div class="tech-card">
339
+ <h4>Data Processing</h4>
340
+ <ul>
341
+ <li>Pandas 2.0+</li>
342
+ <li>NumPy 1.24+</li>
343
+ <li>CSV-based Storage</li>
344
+ <li>In-Memory Caching</li>
345
+ </ul>
346
+ </div>
347
+
348
+ <div class="tech-card">
349
+ <h4>Frontend</h4>
350
+ <ul>
351
+ <li>HTML5 + CSS3</li>
352
+ <li>Vanilla JavaScript</li>
353
+ <li>Inter Font Family</li>
354
+ <li>SVG Graphics</li>
355
+ </ul>
356
+ </div>
357
+
358
+ <div class="tech-card">
359
+ <h4>Cloud Deployment</h4>
360
+ <ul>
361
+ <li>Hugging Face Spaces</li>
362
+ <li>Docker Container</li>
363
+ <li>Git Version Control</li>
364
+ </ul>
365
+ </div>
366
+
367
+ <div class="tech-card">
368
+ <h4>Testing & QA</h4>
369
+ <ul>
370
+ <li>Python Requests</li>
371
+ <li>Custom Test Suite</li>
372
+ <li>Load Testing Scripts</li>
373
+ <li>Concurrent Testing</li>
374
+ </ul>
375
+ </div>
376
+ </div>
377
+ </div>
378
+
379
+ <!-- Technical Specifications -->
380
+ <div class="section">
381
+ <h2>📊 Technical Specifications</h2>
382
+
383
+ <table class="specs-table">
384
+ <thead>
385
+ <tr>
386
+ <th>Component</th>
387
+ <th>Specification</th>
388
+ <th>Purpose</th>
389
+ </tr>
390
+ </thead>
391
+ <tbody>
392
+ <tr>
393
+ <td><strong>LLM Model</strong></td>
394
+ <td>TinyLlama 1.1B (Q4_K_M quantized)</td>
395
+ <td>General chat, context-aware responses</td>
396
+ </tr>
397
+ <tr>
398
+ <td><strong>Model Size</strong></td>
399
+ <td>~500 MB (GGUF format)</td>
400
+ <td>Optimized for CPU inference</td>
401
+ </tr>
402
+ <tr>
403
+ <td><strong>Forecasting Algorithm</strong></td>
404
+ <td>ARIMA (AutoRegressive Integrated Moving Average)</td>
405
+ <td>Time-series demand prediction</td>
406
+ </tr>
407
+ <tr>
408
+ <td><strong>Intent Recognition</strong></td>
409
+ <td>Rule-based pattern matching + Regex</td>
410
+ <td>Fast, deterministic intent parsing</td>
411
+ </tr>
412
+ <tr>
413
+ <td><strong>API Protocol</strong></td>
414
+ <td>REST (JSON)</td>
415
+ <td>Standard HTTP communication</td>
416
+ </tr>
417
+ <tr>
418
+ <td><strong>Data Format</strong></td>
419
+ <td>CSV (UTF-8)</td>
420
+ <td>Simple, portable data storage</td>
421
+ </tr>
422
+ <tr>
423
+ <td><strong>PDF Generation</strong></td>
424
+ <td>FPDF Library</td>
425
+ <td>Report generation</td>
426
+ </tr>
427
+ <tr>
428
+ <td><strong>Response Time</strong></td>
429
+ <td>&lt;2s (rule-based), &lt;15s (LLM)</td>
430
+ <td>User experience optimization</td>
431
+ </tr>
432
+ <tr>
433
+ <td><strong>Concurrent Users</strong></td>
434
+ <td>5-10 (free tier), 50+ (paid)</td>
435
+ <td>Scalability</td>
436
+ </tr>
437
+ <tr>
438
+ <td><strong>Memory Requirement</strong></td>
439
+ <td>~2 GB RAM (minimum)</td>
440
+ <td>Model + application overhead</td>
441
+ </tr>
442
+ </tbody>
443
+ </table>
444
+ </div>
445
+
446
+ <!-- Process Flow -->
447
+ <div class="section">
448
+ <h2>🔄 Complete Process Flow</h2>
449
+
450
+ <div class="flow-diagram">
451
+ <div class="flow-step" data-step="1">
452
+ <h4>User Input</h4>
453
+ <p><strong>Component:</strong> index.html (Frontend)</p>
454
+ <p><strong>Action:</strong> User types query in chat interface</p>
455
+ <p><strong>Technology:</strong> JavaScript event listener (keypress/click)</p>
456
+ <div class="code-block">
457
+ userInput.addEventListener('keypress', function (e) {
458
+ if (e.key === 'Enter') sendMessage();
459
+ });</div>
460
+ </div>
461
+
462
+ <div class="flow-step" data-step="2">
463
+ <h4>API Request</h4>
464
+ <p><strong>Component:</strong> JavaScript Fetch API</p>
465
+ <p><strong>Action:</strong> POST request to /api/chat endpoint</p>
466
+ <p><strong>Payload:</strong> JSON with user message</p>
467
+ <div class="code-block">
468
+ fetch('/api/chat', {
469
+ method: 'POST',
470
+ headers: {'Content-Type': 'application/json'},
471
+ body: JSON.stringify({message: text})
472
+ });</div>
473
+ </div>
474
+
475
+ <div class="flow-step" data-step="3">
476
+ <h4>Intent Parsing</h4>
477
+ <p><strong>Component:</strong> intent_parser.py</p>
478
+ <p><strong>Action:</strong> Analyze query using regex patterns</p>
479
+ <p><strong>Output:</strong> Intent type + extracted entities (item_code, quantity, etc.)</p>
480
+ <div class="code-block">
481
+ parsed = parser.parse(message)
482
+ # Returns: {
483
+ # "intent": "demand_forecast",
484
+ # "item_code": "BMS0015",
485
+ # "horizon_days": 30
486
+ # }</div>
487
+ </div>
488
+
489
+ <div class="flow-step" data-step="4">
490
+ <h4>Intent Routing</h4>
491
+ <p><strong>Component:</strong> main.py (FastAPI)</p>
492
+ <p><strong>Action:</strong> Route to appropriate handler based on intent</p>
493
+ <p><strong>Intents:</strong> demand_forecast, item_details, check_inventory, supplier_info,
494
+ create_requisition, system_status, generate_report, general_chat</p>
495
+ </div>
496
+
497
+ <div class="flow-step" data-step="5">
498
+ <h4>Business Logic Execution</h4>
499
+ <p><strong>Components:</strong> Multiple modules based on intent</p>
500
+ <p><strong>Actions:</strong></p>
501
+ <ul style="margin-left: 20px; margin-top: 10px;">
502
+ <li><strong>Forecasting:</strong> forecasting.py → ARIMA model → Predict demand</li>
503
+ <li><strong>Data Retrieval:</strong> data_loader.py → Load CSV → Query data</li>
504
+ <li><strong>LLM Response:</strong> llm_engine.py → TinyLlama → Generate text</li>
505
+ <li><strong>PDF Generation:</strong> pdf_generator.py → FPDF → Create report</li>
506
+ </ul>
507
+ </div>
508
+
509
+ <div class="flow-step" data-step="6">
510
+ <h4>Response Formatting</h4>
511
+ <p><strong>Component:</strong> main.py</p>
512
+ <p><strong>Action:</strong> Format response as JSON</p>
513
+ <p><strong>Structure:</strong> {intent, answer, forecast (optional), pdf_link (optional)}</p>
514
+ <div class="code-block">
515
+ return {
516
+ "intent": "demand_forecast",
517
+ "answer": "Forecast for BMS0015...",
518
+ "forecast": [{date: "2025-01-01", qty: 150}, ...]
519
+ }</div>
520
+ </div>
521
+
522
+ <div class="flow-step" data-step="7">
523
+ <h4>Frontend Rendering</h4>
524
+ <p><strong>Component:</strong> index.html (JavaScript)</p>
525
+ <p><strong>Action:</strong> Parse JSON, render message bubble, display tables/charts</p>
526
+ <p><strong>Features:</strong> HTML sanitization, table rendering, download buttons</p>
527
+ <div class="code-block">
528
+ addMessage(botHtml, 'bot', true);
529
+ if (data.forecast) {
530
+ botHtml += renderForecastTable(data.forecast);
531
+ }</div>
532
+ </div>
533
+
534
+ <div class="flow-step" data-step="8">
535
+ <h4>User Interaction</h4>
536
+ <p><strong>Component:</strong> Frontend UI</p>
537
+ <p><strong>Action:</strong> Display response with icons, animations, and interactive elements
538
+ </p>
539
+ <p><strong>Features:</strong> Smooth scrolling, copy text, download PDFs, click links</p>
540
+ </div>
541
+ </div>
542
+ </div>
543
+
544
+ <!-- Detailed Component Breakdown -->
545
+ <div class="section">
546
+ <h2>🔧 Component Breakdown</h2>
547
+
548
+ <h3>1. Intent Parser (intent_parser.py)</h3>
549
+ <div class="code-block">
550
+ # Pattern Matching Examples:
551
+ - "forecast" → demand_forecast
552
+ - "inventory|stock" → check_inventory
553
+ - "supplier|vendor" → supplier_info
554
+ - "order|buy|purchase" → create_requisition
555
+ - Item Code Extraction: r'\b([a-z]{2}\d{4})\b'
556
+ - Quantity Extraction: r'\b(\d+)\s*(?:units|pcs)?'
557
+ - Horizon Parsing: "next month" → 30 days</div>
558
+
559
+ <h3>2. Data Loader (data_loader.py)</h3>
560
+ <div class="code-block">
561
+ # Data Sources:
562
+ - items.csv: Product catalog (item_code, description, price)
563
+ - demand_history.csv: Historical sales data
564
+ - inventory.csv: Stock levels by location
565
+ - suppliers.csv: Supplier information
566
+ - requisitions.csv: Order tracking
567
+
568
+ # Methods:
569
+ - load_data(): Initialize all DataFrames
570
+ - get_item_details(item_code): Retrieve product info
571
+ - get_inventory(item_code): Check stock levels
572
+ - create_requisition(item_code, qty): Generate order</div>
573
+
574
+ <h3>3. Forecasting Engine (forecasting.py)</h3>
575
+ <div class="code-block">
576
+ # ARIMA Process:
577
+ 1. Load historical demand data
578
+ 2. Aggregate by date
579
+ 3. Fit ARIMA model (order=(1,1,1))
580
+ 4. Generate forecast for N days
581
+ 5. Return predictions with dates
582
+
583
+ # Output Format:
584
+ [
585
+ {"date": "2025-01-01", "qty": 150},
586
+ {"date": "2025-01-02", "qty": 148},
587
+ ...
588
+ ]</div>
589
+
590
+ <h3>4. LLM Engine (llm_engine.py)</h3>
591
+ <div class="code-block">
592
+ # Model Configuration:
593
+ - Model: TinyLlama-1.1B-Chat-v1.0 (GGUF)
594
+ - Context Length: 2048 tokens
595
+ - Temperature: 0.1 (deterministic)
596
+ - Max Tokens: 150
597
+ - Stop Tokens: ["</s>", "User:"]</div>
598
+ </div>
599
+ </div>
600
+ </div>
601
+ </body>
602
+
603
+ </html>
app/config.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
4
+ DATA_DIR = os.path.join(BASE_DIR, "data")
5
+ ITEMS_FILE = os.path.join(DATA_DIR, "items.csv")
6
+ DEMAND_FILE = os.path.join(DATA_DIR, "demand_history.csv")
7
+
8
+ DEFAULT_HORIZON = 30
9
+ DEFAULT_LOCATION = "NA"
app/data_loader.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.config import ITEMS_FILE, DEMAND_FILE
2
+ import os
3
+ import pandas as pd
4
+
5
+ INVENTORY_FILE = os.path.join(os.path.dirname(ITEMS_FILE), "inventory.csv")
6
+ SUPPLIERS_FILE = os.path.join(os.path.dirname(ITEMS_FILE), "suppliers.csv")
7
+ REQUISITIONS_FILE = os.path.join(os.path.dirname(ITEMS_FILE), "requisitions.csv")
8
+
9
+ class DataLoader:
10
+ def __init__(self):
11
+ self.items_df = None
12
+ self.demand_df = None
13
+ self.inventory_df = None
14
+ self.suppliers_df = None
15
+ self.requisitions_df = None
16
+ self.load_data()
17
+
18
+ def load_data(self):
19
+ if os.path.exists(ITEMS_FILE):
20
+ self.items_df = pd.read_csv(ITEMS_FILE)
21
+ self.items_df['item_code'] = self.items_df['item_code'].str.upper()
22
+ else:
23
+ self.items_df = pd.DataFrame(columns=['item_code', 'description', 'uom', 'list_price'])
24
+
25
+ if os.path.exists(DEMAND_FILE):
26
+ self.demand_df = pd.read_csv(DEMAND_FILE, keep_default_na=False)
27
+ self.demand_df['date'] = pd.to_datetime(self.demand_df['date'])
28
+ self.demand_df['item_code'] = self.demand_df['item_code'].str.upper()
29
+ if 'region' in self.demand_df.columns:
30
+ self.demand_df['region'] = self.demand_df['region'].str.upper()
31
+ else:
32
+ self.demand_df = pd.DataFrame(columns=['item_code', 'date', 'quantity', 'region'])
33
+
34
+ if os.path.exists(INVENTORY_FILE):
35
+ self.inventory_df = pd.read_csv(INVENTORY_FILE)
36
+ self.inventory_df.rename(columns={'on_hand': 'qty_on_hand'}, inplace=True)
37
+ self.inventory_df['item_code'] = self.inventory_df['item_code'].str.upper()
38
+ if 'region' in self.inventory_df.columns:
39
+ self.inventory_df['region'] = self.inventory_df['region'].str.upper()
40
+
41
+ # Calculate Status
42
+ def get_status(row):
43
+ if row['available'] < 50: return 'Critical'
44
+ elif row['available'] < 100: return 'Low Stock'
45
+ else: return 'In Stock'
46
+
47
+ if 'available' in self.inventory_df.columns:
48
+ self.inventory_df['status'] = self.inventory_df.apply(get_status, axis=1)
49
+ else:
50
+ self.inventory_df['status'] = 'Unknown'
51
+ else:
52
+ self.inventory_df = pd.DataFrame(columns=['item_code', 'region', 'qty_on_hand', 'reorder_point', 'status'])
53
+
54
+ if os.path.exists(SUPPLIERS_FILE):
55
+ self.suppliers_df = pd.read_csv(SUPPLIERS_FILE)
56
+ # self.suppliers_df['item_code'] = self.suppliers_df['item_code'].str.upper() # Removed as item_code is not in suppliers.csv
57
+ else:
58
+ self.suppliers_df = pd.DataFrame(columns=['id', 'name', 'lead_time', 'email'])
59
+
60
+ if os.path.exists(REQUISITIONS_FILE):
61
+ self.requisitions_df = pd.read_csv(REQUISITIONS_FILE)
62
+ else:
63
+ self.requisitions_df = pd.DataFrame(columns=['req_id', 'item_code', 'qty', 'date', 'status'])
64
+
65
+ def get_item(self, item_code):
66
+ if self.items_df is None or item_code is None:
67
+ return None
68
+ item = self.items_df[self.items_df['item_code'] == item_code.upper()]
69
+ if not item.empty:
70
+ return item.iloc[0].to_dict()
71
+ return None
72
+
73
+ def get_time_series(self, item_code, location=None):
74
+ if self.demand_df is None:
75
+ return pd.DataFrame()
76
+
77
+ mask = (self.demand_df['item_code'] == item_code.upper())
78
+ if location:
79
+ mask &= (self.demand_df['region'] == location.upper())
80
+
81
+ df_filtered = self.demand_df[mask].copy()
82
+
83
+ if df_filtered.empty:
84
+ return pd.DataFrame()
85
+
86
+ df_grouped = df_filtered.groupby('date')['quantity'].sum().reset_index()
87
+ df_grouped = df_grouped.sort_values('date')
88
+ df_grouped.set_index('date', inplace=True)
89
+ df_grouped = df_grouped.asfreq('D', fill_value=0)
90
+ return df_grouped
91
+
92
+ def get_inventory(self, item_code, location=None):
93
+ if self.inventory_df is None: return []
94
+ mask = (self.inventory_df['item_code'] == item_code.upper())
95
+ if location:
96
+ mask &= (self.inventory_df['region'] == location.upper())
97
+ return self.inventory_df[mask].to_dict('records')
98
+
99
+ def get_supplier(self, item_code):
100
+ if self.suppliers_df is None or self.items_df is None: return []
101
+
102
+ # Find supplier_id from items
103
+ item_row = self.items_df[self.items_df['item_code'] == item_code.upper()]
104
+ if item_row.empty: return []
105
+
106
+ supplier_id = item_row.iloc[0]['supplier_id']
107
+
108
+ # Find supplier details
109
+ mask = (self.suppliers_df['id'] == supplier_id)
110
+ suppliers = self.suppliers_df[mask].copy()
111
+
112
+ # Rename columns to match what main.py expects
113
+ suppliers.rename(columns={
114
+ 'name': 'supplier_name',
115
+ 'lead_time': 'lead_time_days',
116
+ 'email': 'contact_email'
117
+ }, inplace=True)
118
+
119
+ return suppliers.to_dict('records')
120
+
121
+ def create_requisition(self, item_code, qty):
122
+ import datetime
123
+ import random
124
+
125
+ req_id = f"REQ{random.randint(1000, 9999)}"
126
+ new_row = {
127
+ "req_id": req_id,
128
+ "item_code": item_code.upper(),
129
+ "qty": qty,
130
+ "date": datetime.date.today().strftime("%Y-%m-%d"),
131
+ "status": "Pending"
132
+ }
133
+
134
+ # Append to DataFrame
135
+ self.requisitions_df = pd.concat([self.requisitions_df, pd.DataFrame([new_row])], ignore_index=True)
136
+
137
+ # Save to CSV
138
+ self.requisitions_df.to_csv(REQUISITIONS_FILE, index=False)
139
+ return req_id
140
+
141
+ def get_alerts(self):
142
+ if self.inventory_df is None: return []
143
+ # Filter for Critical or Low Stock
144
+ mask = self.inventory_df['status'].isin(['Critical', 'Low Stock'])
145
+ return self.inventory_df[mask].to_dict('records')
146
+
147
+ def get_items(self):
148
+ if self.items_df is None: return []
149
+ return self.items_df[['item_code', 'description']].to_dict('records')
150
+
151
+ # Global instance
152
+ loader = DataLoader()
app/forecasting.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from statsmodels.tsa.arima.model import ARIMA
4
+ from app.data_loader import loader
5
+ import warnings
6
+
7
+ # Suppress statsmodels warnings for cleaner output
8
+ warnings.filterwarnings("ignore")
9
+
10
+ def forecast_demand(item_code, horizon_days, location=None):
11
+ """
12
+ Generates a demand forecast using ARIMA.
13
+ Returns a list of dicts: [{"date": "YYYY-MM-DD", "qty": 123}, ...]
14
+ """
15
+ ts_data = loader.get_time_series(item_code, location)
16
+
17
+ if ts_data.empty or len(ts_data) < 10:
18
+ return [] # Not enough data to forecast
19
+
20
+ try:
21
+ # Simple ARIMA model (1,1,1) is often a good baseline for non-stationary data
22
+ # For a robust system, we would use auto_arima or grid search, but this is a POC.
23
+ model = ARIMA(ts_data['quantity'], order=(5,1,0))
24
+ model_fit = model.fit()
25
+
26
+ forecast_result = model_fit.forecast(steps=horizon_days)
27
+
28
+ forecast_list = []
29
+ start_date = ts_data.index[-1] + pd.Timedelta(days=1)
30
+
31
+ for i, val in enumerate(forecast_result):
32
+ date = start_date + pd.Timedelta(days=i)
33
+ # Ensure non-negative forecast
34
+ qty = int(max(0, round(val)))
35
+ forecast_list.append({
36
+ "date": date.strftime('%Y-%m-%d'),
37
+ "qty": qty
38
+ })
39
+
40
+ return forecast_list
41
+ except Exception as e:
42
+ print(f"Forecasting error for {item_code}: {e}")
43
+ return []
app/intent_parser.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from app.data_loader import loader
3
+
4
+ class IntentParser:
5
+ def __init__(self):
6
+ pass
7
+
8
+ def parse(self, text):
9
+ text = text.lower()
10
+
11
+ response = {
12
+ "intent": "general_chat", # Default to LLM
13
+ "item_code": None,
14
+ "location": None,
15
+ "horizon_days": None,
16
+ "extra": {}
17
+ }
18
+
19
+ # 1. Detect Intent (order matters - more specific checks first)
20
+ if any(x in text for x in ["list items", "show catalog", "what items", "available items", "list available", "show items"]):
21
+ response["intent"] = "list_items"
22
+ elif any(x in text for x in ["forecast", "demand", "predict", "future", "sales"]):
23
+ response["intent"] = "demand_forecast"
24
+ elif any(x in text for x in ["details", "info", "about", "price", "description"]):
25
+ response["intent"] = "item_details"
26
+ elif any(x in text for x in ["inventory", "stock", "how many", "available", "on hand"]):
27
+ response["intent"] = "check_inventory"
28
+ elif any(x in text for x in ["supplier", "vendor", "who supplies", "lead time"]):
29
+ response["intent"] = "supplier_info"
30
+ elif any(x in text for x in ["buy", "order", "purchase", "requisition"]):
31
+ response["intent"] = "create_requisition"
32
+ elif any(x in text for x in ["status", "alerts", "dashboard", "overview"]):
33
+ response["intent"] = "system_status"
34
+ elif any(x in text for x in ["pdf", "download", "report", "file"]):
35
+ response["intent"] = "generate_report"
36
+
37
+ # 2. Extract Item Code
38
+ # Look for patterns like FS1234, BMS0001, etc. (2-3 letters + 4 digits)
39
+ item_match = re.search(r'\b([a-z]{2,3}\d{4})\b', text)
40
+ if item_match:
41
+ response["item_code"] = item_match.group(1).upper()
42
+ else:
43
+ # Fallback: check against known items in DB
44
+ if loader.items_df is not None:
45
+ known_items = loader.items_df['item_code'].tolist()
46
+ for item in known_items:
47
+ if item.lower() in text:
48
+ response["item_code"] = item
49
+ break
50
+
51
+ # 2.5 Extract Quantity (for orders)
52
+ qty_match = re.search(r'\b(\d+)\s*(?:units|pcs|pieces)?\b', text)
53
+ if qty_match:
54
+ # Avoid confusing horizon days with quantity if possible
55
+ # If "days" is not next to it, assume qty
56
+ if "day" not in text[qty_match.end():qty_match.end()+5]:
57
+ response["extra"]["qty"] = int(qty_match.group(1))
58
+
59
+ # 3. Extract Location
60
+ if re.search(r'\b(na|north america|usa|us)\b', text):
61
+ response["location"] = "NA"
62
+ elif re.search(r'\b(eu|europe|uk|germany)\b', text):
63
+ response["location"] = "EU"
64
+ elif re.search(r'\b(apac|asia|china|india)\b', text):
65
+ response["location"] = "APAC"
66
+
67
+ # 4. Extract Horizon
68
+ response["horizon_days"] = self._parse_horizon(text)
69
+
70
+ return response
71
+
72
+ def _parse_horizon(self, text):
73
+ days = None
74
+
75
+ # Explicit days
76
+ days_match = re.search(r'(\d+)\s*days?', text)
77
+ if days_match:
78
+ return int(days_match.group(1))
79
+
80
+ # Weeks
81
+ weeks_match = re.search(r'(\d+)\s*weeks?', text)
82
+ if weeks_match:
83
+ return int(weeks_match.group(1)) * 7
84
+
85
+ # Months
86
+ months_match = re.search(r'(\d+)\s*months?', text)
87
+ if months_match:
88
+ return int(months_match.group(1)) * 30
89
+
90
+ # Keywords
91
+ if "next week" in text:
92
+ return 7
93
+ if "next month" in text:
94
+ return 30
95
+ if "next year" in text:
96
+ return 365
97
+ if "tomorrow" in text:
98
+ return 1
99
+
100
+ return days
101
+
102
+ parser = IntentParser()
app/llm_engine.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ from ctransformers import AutoModelForCausalLM
4
+
5
+ MODEL_REPO = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF"
6
+ MODEL_FILE = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
7
+ MODEL_TYPE = "llama"
8
+
9
+ PROFANITY_LIST = ['damn', 'hell', 'crap', 'stupid', 'idiot', 'dumb', 'suck']
10
+ OFF_TOPIC_KEYWORDS = ['weather', 'sports', 'politics', 'religion', 'movie', 'game', 'recipe', 'joke', 'story', 'music', 'celebrity']
11
+
12
+ class LLMEngine:
13
+ def __init__(self):
14
+ self.model = None
15
+ self.context = ""
16
+ self.load_context()
17
+
18
+ def load_context(self):
19
+ try:
20
+ context_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "company_context.txt")
21
+ if os.path.exists(context_path):
22
+ with open(context_path, "r", encoding="utf-8") as f:
23
+ self.context = f.read()
24
+ except Exception as e:
25
+ print(f"Error loading context: {e}")
26
+
27
+ def load_model(self):
28
+ if self.model is None:
29
+ print("Loading LLM...")
30
+ try:
31
+ self.model = AutoModelForCausalLM.from_pretrained(MODEL_REPO, model_file=MODEL_FILE, model_type=MODEL_TYPE, context_length=2048, gpu_layers=0)
32
+ print("LLM Loaded.")
33
+ except Exception as e:
34
+ print(f"Failed to load LLM: {e}")
35
+
36
+ def check_profanity(self, text):
37
+ for word in PROFANITY_LIST:
38
+ if re.search(r'\b' + word + r'\b', text.lower()):
39
+ return True
40
+ return False
41
+
42
+ def check_off_topic(self, text):
43
+ for keyword in OFF_TOPIC_KEYWORDS:
44
+ if keyword in text.lower():
45
+ return True
46
+ return False
47
+
48
+ def generate_response(self, user_query):
49
+ if self.check_profanity(user_query):
50
+ return "I'm here to assist with business operations. Please keep our conversation professional."
51
+ if self.check_off_topic(user_query):
52
+ return "I specialize in demand forecasting, inventory management, and order processing. How can I help you with these?"
53
+
54
+ greetings = ["hi", "hello", "hey", "greetings", "good morning", "good afternoon", "good evening"]
55
+ if user_query.lower().strip().strip("!.,?") in greetings:
56
+ return "Hello! I'm BMS AI Assistant. I can help you with:\n• Demand Forecasting\n• Inventory Checks\n• Supplier Information\n• Order Requisitions\n• PDF Reports\n\nWhat would you like to know?"
57
+
58
+ if any(w in user_query.lower() for w in ['capabilities', 'what can you do', 'help me', 'functions']):
59
+ return "I can assist you with:\n1. Demand Forecasting\n2. Inventory Management\n3. Supplier Information\n4. Order Processing\n5. PDF Reports\n\nTry asking: 'Forecast for BMS0015'"
60
+
61
+ if any(w in user_query.lower() for w in ['who made you', 'who developed', 'who created', 'who built']):
62
+ return "I'm BMS AI Assistant, developed to help you manage inventory and forecast demand efficiently."
63
+
64
+ if any(p in user_query.lower() for p in ['how are you', 'how do you do', 'how is it going']):
65
+ return "I'm functioning well and ready to assist you! How can I help with your inventory or forecasting needs today?"
66
+
67
+ if self.model is None:
68
+ self.load_model()
69
+ if self.model is None:
70
+ return "I'm sorry, I couldn't load my language model. Please try asking about specific items."
71
+
72
+ system_prompt = (
73
+ "You are BMS AI Assistant, a helpful and professional AI for Business Management Systems. "
74
+ "Your goal is to assist users with demand forecasting, inventory management, and supplier information. "
75
+ "Answer questions based ONLY on the provided context. If the answer is not in the context, politely state that you don't have that information. "
76
+ "Be concise but friendly."
77
+ )
78
+ full_prompt = f"<|system|>\n{system_prompt}\n\nContext:\n{self.context}</s>\n<|user|>\n{user_query}</s>\n<|assistant|>"
79
+
80
+ try:
81
+ response = self.model(
82
+ full_prompt,
83
+ max_new_tokens=150,
84
+ temperature=0.1,
85
+ repetition_penalty=1.1,
86
+ top_k=40,
87
+ top_p=0.9,
88
+ stop=["</s>", "<|user|>", "<|system|>"]
89
+ )
90
+
91
+ if "<|system|>" in response:
92
+ response = response.split("<|system|>")[0]
93
+
94
+ return response.strip()
95
+ except Exception as e:
96
+ return f"Error generating response: {e}"
97
+
98
+ llm = LLMEngine()
app/llm_engine_backup.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ from ctransformers import AutoModelForCausalLM
4
+ from huggingface_hub import hf_hub_download
5
+
6
+ # Configuration
7
+ MODEL_REPO = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF"
8
+ MODEL_FILE = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
9
+ MODEL_TYPE = "llama"
10
+
11
+ # Content Moderation Lists
12
+ PROFANITY_LIST = [
13
+ 'damn', 'hell', 'crap', 'stupid', 'idiot', 'dumb', 'suck',
14
+ ]
15
+
16
+ OFF_TOPIC_KEYWORDS = [
17
+ 'weather', 'sports', 'politics', 'religion', 'movie', 'game',
18
+ 'recipe', 'joke', 'story', 'music', 'celebrity'
19
+ ]
20
+
21
+ class LLMEngine:
22
+ def __init__(self):
23
+ self.model = None
24
+ self.context = ""
25
+ self.load_context()
26
+
27
+ def load_context(self):
28
+ try:
29
+ context_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "company_context.txt")
30
+ if os.path.exists(context_path):
31
+ with open(context_path, "r", encoding="utf-8") as f:
32
+ self.context = f.read()
33
+ except Exception as e:
34
+ print(f"Error loading context: {e}")
35
+
36
+ def load_model(self):
37
+ if self.model is None:
38
+ print("Loading LLM... this may take a moment.")
39
+ try:
40
+ self.model = AutoModelForCausalLM.from_pretrained(
41
+ MODEL_REPO,
42
+ model_file=MODEL_FILE,
43
+ model_type=MODEL_TYPE,
44
+ context_length=2048,
45
+ gpu_layers=0
46
+ )
47
+ print("LLM Loaded successfully.")
48
+ except Exception as e:
49
+ print(f"Failed to load LLM: {e}")
50
+
51
+ def check_profanity(self, text):
52
+ """Check for inappropriate language"""
53
+ text_lower = text.lower()
54
+ for word in PROFANITY_LIST:
55
+ if re.search(r'\b' + word + r'\b', text_lower):
56
+ return True
57
+ return False
58
+
59
+ def check_off_topic(self, text):
60
+ """Check if query is off-topic"""
61
+ text_lower = text.lower()
62
+ for keyword in OFF_TOPIC_KEYWORDS:
63
+ if keyword in text_lower:
64
+ return True
65
+ return False
66
+
67
+ def generate_response(self, user_query):
68
+ """Generate response with content moderation"""
69
+
70
+ # Content Moderation
71
+ if self.check_profanity(user_query):
72
+ return ("I'm here to assist with business operations. "
73
+ "Please keep our conversation professional and focused on "
74
+ "demand forecasting, inventory management, or order processing.")
75
+
76
+ # Off-topic detection
77
+ if self.check_off_topic(user_query):
78
+ return ("I specialize in demand forecasting, inventory management, and order processing. "
79
+ "How can I help you with these business functions?")
80
+
81
+ # Greetings - Enhanced
82
+ greetings = ["hi", "hello", "hey", "greetings", "good morning", "good afternoon", "good evening"]
83
+ query_clean = user_query.lower().strip().strip("!.,?")
84
+ if query_clean in greetings:
85
+ return ("Hello! I'm BMS AI Assistant. I can help you with:\n"
86
+ "• Demand Forecasting\n"
87
+ "• Inventory Checks\n"
88
+ "• Supplier Information\n"
89
+ "• Order Requisitions\n"
90
+ "• PDF Reports\n\n"
91
+ "What would you like to know?")
92
+
93
+ # Capabilities query
94
+ if any(word in user_query.lower() for word in ['capabilities', 'what can you do', 'help me', 'functions']):
95
+ return ("I can assist you with:\n\n"
96
+ "1. Demand Forecasting - Predict future demand for items\n"
97
+ "2. Inventory Management - Check stock levels across warehouses\n"
98
+ "3. Supplier Information - Get supplier details and lead times\n"
99
+ "4. Order Processing - Create purchase requisitions\n"
100
+ "5. PDF Reports - Download detailed reports\n\n"
101
+ "Try asking: 'Forecast for BMS0015' or 'Check inventory for BMS0042'")
102
+
103
+ # Who developed you
104
+ if any(word in user_query.lower() for word in ['who made you', 'who developed', 'who created', 'who built']):
105
+ return "I'm BMS AI Assistant, developed to help you manage inventory and forecast demand efficiently."
106
+
107
+ # How are you
108
+ if any(phrase in user_query.lower() for phrase in ['how are you', 'how do you do', 'how is it going']):
109
+ return "I'm functioning well and ready to assist you! How can I help with your inventory or forecasting needs today?"
110
+
111
+ # Load model if needed
112
+ if self.model is None:
113
+ self.load_model()
114
+
115
+ if self.model is None:
116
+ return "I'm sorry, I couldn't load my language model. Please try asking about specific items or inventory."
117
+
118
+ # Construct Prompt
119
+ system_prompt = (
120
+ "You are BMS AI Assistant for business operations. "
121
+ "Answer questions using ONLY the provided context. "
122
+ "If the answer is not in the context, say you don't know. "
123
+ "Do not make up facts. "
124
+ "Keep answers concise (under 3 sentences). "
125
+ "Be professional and helpful."
126
+ )
127
+
128
+ full_prompt = f"<|system|>\n{system_prompt}\n\nContext:\n{self.context}</s>\n
app/llm_engine_old.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ from ctransformers import AutoModelForCausalLM
4
+ from huggingface_hub import hf_hub_download
5
+
6
+ # Configuration
7
+ MODEL_REPO = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF"
8
+ MODEL_FILE = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
9
+ MODEL_TYPE = "llama"
10
+
11
+ # Content Moderation Lists
12
+ PROFANITY_LIST = [
13
+ 'damn', 'hell', 'crap', 'stupid', 'idiot', 'dumb', 'suck',
14
+ ]
15
+
16
+ OFF_TOPIC_KEYWORDS = [
17
+ 'weather', 'sports', 'politics', 'religion', 'movie', 'game',
18
+ 'recipe', 'joke', 'story', 'music', 'celebrity'
19
+ ]
20
+
21
+ class LLMEngine:
22
+ def __init__(self):
23
+ self.model = None
24
+ self.context = ""
25
+ self.load_context()
26
+
27
+ def load_context(self):
28
+ try:
29
+ context_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "company_context.txt")
30
+ if os.path.exists(context_path):
31
+ with open(context_path, "r", encoding="utf-8") as f:
32
+ self.context = f.read()
33
+ except Exception as e:
34
+ print(f"Error loading context: {e}")
35
+
36
+ def load_model(self):
37
+ if self.model is None:
38
+ print("Loading LLM... this may take a moment.")
39
+ try:
40
+ self.model = AutoModelForCausalLM.from_pretrained(
41
+ MODEL_REPO,
42
+ model_file=MODEL_FILE,
43
+ model_type=MODEL_TYPE,
44
+ context_length=2048,
45
+ gpu_layers=0
46
+ )
47
+ print("LLM Loaded successfully.")
48
+ except Exception as e:
49
+ print(f"Failed to load LLM: {e}")
50
+
51
+ def check_profanity(self, text):
52
+ """Check for inappropriate language"""
53
+ text_lower = text.lower()
54
+ for word in PROFANITY_LIST:
55
+ if re.search(r'\b' + word + r'\b', text_lower):
56
+ return True
57
+ return False
58
+
59
+ def check_off_topic(self, text):
60
+ """Check if query is off-topic"""
61
+ text_lower = text.lower()
62
+ for keyword in OFF_TOPIC_KEYWORDS:
63
+ if keyword in text_lower:
64
+ return True
65
+ return False
66
+
67
+ def generate_response(self, user_query):
68
+ """Generate response with content moderation"""
69
+
70
+ # Content Moderation
71
+ if self.check_profanity(user_query):
72
+ return ("I'm here to assist with business operations. "
73
+ "Please keep our conversation professional and focused on "
74
+ "demand forecasting, inventory management, or order processing.")
75
+
76
+ # Off-topic detection
77
+ if self.check_off_topic(user_query):
78
+ return ("I specialize in demand forecasting, inventory management, and order processing. "
79
+ "How can I help you with these business functions?")
80
+
81
+ # Greetings - Enhanced
82
+ greetings = ["hi", "hello", "hey", "greetings", "good morning", "good afternoon", "good evening"]
83
+ query_clean = user_query.lower().strip().strip("!.,?")
84
+ if query_clean in greetings:
85
+ return ("Hello! I'm BMS AI Assistant. I can help you with:\n"
86
+ "• Demand Forecasting\n"
87
+ "• Inventory Checks\n"
88
+ "• Supplier Information\n"
89
+ "• Order Requisitions\n"
90
+ "• PDF Reports\n\n"
91
+ "What would you like to know?")
92
+
93
+ # Capabilities query
94
+ if any(word in user_query.lower() for word in ['capabilities', 'what can you do', 'help me', 'functions']):
95
+ return ("I can assist you with:\n\n"
96
+ "1. Demand Forecasting - Predict future demand for items\n"
97
+ "2. Inventory Management - Check stock levels across warehouses\n"
98
+ "3. Supplier Information - Get supplier details and lead times\n"
99
+ "4. Order Processing - Create purchase requisitions\n"
100
+ "5. PDF Reports - Download detailed reports\n\n"
101
+ "Try asking: 'Forecast for BMS0015' or 'Check inventory for BMS0042'")
102
+
103
+ # Who developed you
104
+ if any(word in user_query.lower() for word in ['who made you', 'who developed', 'who created', 'who built']):
105
+ return "I'm BMS AI Assistant, developed to help you manage inventory and forecast demand efficiently."
106
+
107
+ # How are you
108
+ if any(phrase in user_query.lower() for phrase in ['how are you', 'how do you do', 'how is it going']):
109
+ return "I'm functioning well and ready to assist you! How can I help with your inventory or forecasting needs today?"
110
+
111
+ # Load model if needed
112
+ if self.model is None:
113
+ self.load_model()
114
+
115
+ if self.model is None:
116
+ return "I'm sorry, I couldn't load my language model. Please try asking about specific items or inventory."
117
+
118
+ # Construct Prompt
119
+ system_prompt = (
120
+ "You are BMS AI Assistant for business operations. "
121
+ "Answer questions using ONLY the provided context. "
122
+ "If the answer is not in the context, say you don't know. "
123
+ "Do not make up facts. "
124
+ "Keep answers concise (under 3 sentences). "
125
+ "Be professional and helpful."
126
+ )
127
+
128
+ full_prompt = f"<|system|>\n{system_prompt}\n\nContext:\n{self.context}</s>\n
app/main.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.staticfiles import StaticFiles
3
+ from fastapi.responses import HTMLResponse, JSONResponse
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from pydantic import BaseModel
6
+ import os
7
+ import logging
8
+
9
+ # Configure logging
10
+ logging.basicConfig(level=logging.INFO)
11
+ logger = logging.getLogger(__name__)
12
+
13
+ from app.intent_parser import parser
14
+ from app.forecasting import forecast_demand
15
+ from app.data_loader import loader
16
+ from app.config import DEFAULT_HORIZON
17
+ from app.llm_engine import llm
18
+ from app.pdf_generator import generate_forecast_pdf, generate_general_pdf
19
+
20
+ app = FastAPI(title="BMS AI Assistant")
21
+
22
+ # CORS
23
+ app.add_middleware(
24
+ CORSMiddleware,
25
+ allow_origins=["*"],
26
+ allow_credentials=True,
27
+ allow_methods=["*"],
28
+ allow_headers=["*"],
29
+ )
30
+
31
+ # Mount Static Files
32
+ # We mount this to serve assets like CSS/JS if they are referenced in the HTML
33
+ static_dir = "/app/static"
34
+ if not os.path.exists(static_dir):
35
+ # Fallback for local testing
36
+ static_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static")
37
+
38
+ if os.path.exists(static_dir):
39
+ app.mount("/static", StaticFiles(directory=static_dir), name="static")
40
+ logger.info(f"Mounted static files from: {static_dir}")
41
+ else:
42
+ logger.warning(f"Static directory not found at {static_dir}")
43
+
44
+ class ChatRequest(BaseModel):
45
+ message: str
46
+
47
+ # Root Endpoint
48
+ @app.get("/", response_class=HTMLResponse)
49
+ async def read_root():
50
+ # Logic: Read the file content and return it.
51
+ possible_paths = [
52
+ "/app/static/index.html", # Docker absolute path
53
+ os.path.join(os.path.dirname(os.path.dirname(__file__)), "static", "index.html"), # Local relative
54
+ "static/index.html" # Simple relative
55
+ ]
56
+
57
+ for path in possible_paths:
58
+ if os.path.exists(path):
59
+ logger.info(f"Serving HTML from: {path}")
60
+ with open(path, "r", encoding="utf-8") as f:
61
+ return HTMLResponse(content=f.read())
62
+
63
+ logger.error("Could not find index.html in any known location.")
64
+ return HTMLResponse(content="<h1>Error: index.html not found</h1>", status_code=500)
65
+
66
+ @app.get("/api/health")
67
+ async def health_check():
68
+ return {"status": "ok", "message": "BMS AI Assistant is running"}
69
+
70
+ # Chat Endpoint
71
+ chat_context = {
72
+ "last_forecast": None,
73
+ "last_answer": None
74
+ }
75
+
76
+ @app.post("/api/chat")
77
+ async def chat_endpoint(request: ChatRequest):
78
+ user_text = request.message
79
+ try:
80
+ parsed = parser.parse(user_text)
81
+ intent = parsed["intent"]
82
+ item_code = parsed["item_code"]
83
+ location = parsed["location"]
84
+ horizon = parsed["horizon_days"]
85
+
86
+ response_data = {
87
+ "intent": intent,
88
+ "answer": "",
89
+ "forecast": []
90
+ }
91
+
92
+ if intent == "demand_forecast":
93
+ if not item_code:
94
+ response_data["answer"] = "I can help with forecasting, but I need to know which item you are interested in."
95
+ else:
96
+ if not horizon: horizon = DEFAULT_HORIZON
97
+ forecast = forecast_demand(item_code, horizon, location)
98
+ response_data["forecast"] = forecast
99
+
100
+ chat_context["last_forecast"] = {"item_code": item_code, "forecast": forecast, "location": location}
101
+
102
+ total = sum(d['qty'] for d in forecast) if forecast else 0
103
+ response_data["answer"] = f"Forecast for {item_code} over next {horizon} days is {total} units."
104
+
105
+ elif intent == "list_items":
106
+ items = loader.get_items()
107
+ msg = "**Available Items:**\n"
108
+ for item in items[:10]:
109
+ msg += f"- {item['item_code']}: {item['description']}\n"
110
+ response_data["answer"] = msg
111
+
112
+ elif intent == "check_inventory":
113
+ if not item_code:
114
+ response_data["answer"] = "Please specify an item code to check inventory for."
115
+ else:
116
+ inv_data = loader.get_inventory(item_code, location)
117
+ if not inv_data:
118
+ response_data["answer"] = f"No inventory data found for {item_code}."
119
+ else:
120
+ msg = f"**Inventory for {item_code}:**\n"
121
+ for record in inv_data:
122
+ loc = record.get('region', 'Unknown')
123
+ qty = record.get('qty_on_hand', 0)
124
+ status = record.get('status', 'Unknown')
125
+ msg += f"- Location: {loc}, On Hand: {qty}, Status: {status}\n"
126
+ response_data["answer"] = msg
127
+ # Update context for potential report generation
128
+ chat_context["last_forecast"] = {"item_code": item_code, "forecast": [], "location": location}
129
+
130
+ elif intent == "generate_report":
131
+ # Check if we have a valid context
132
+ if chat_context.get("last_forecast") and chat_context["last_forecast"].get("item_code"):
133
+ lf = chat_context["last_forecast"]
134
+ # Use the item code from the last interaction
135
+ target_item = lf["item_code"]
136
+
137
+ # If the user explicitly mentioned a different item in this request, use that instead
138
+ if item_code and item_code != target_item:
139
+ target_item = item_code
140
+ # If we don't have forecast data for this new item, we might need to generate it or just produce a generic report
141
+ # For now, let's just warn or proceed with what we have
142
+
143
+ filename = generate_forecast_pdf(target_item, lf.get("forecast", []), lf.get("location"))
144
+ response_data["answer"] = f"Report generated for {target_item}: <a href='/static/reports/{filename}' target='_blank'>Download PDF</a>"
145
+ else:
146
+ response_data["answer"] = "I don't have enough context to generate a report. Please ask for a forecast or inventory check first."
147
+ # Fallback to LLM
148
+ response_data["answer"] = llm.generate_response(user_text)
149
+ chat_context["last_answer"] = response_data["answer"]
150
+
151
+ return JSONResponse(content=response_data)
152
+
153
+ except Exception as e:
154
+ logger.error(f"Error in chat endpoint: {e}")
155
+ return JSONResponse(content={"answer": f"Error processing request: {str(e)}"}, status_code=500)
app/pdf_generator.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fpdf import FPDF
2
+ import os
3
+ from datetime import datetime
4
+
5
+ REPORT_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static", "reports")
6
+ if not os.path.exists(REPORT_DIR):
7
+ os.makedirs(REPORT_DIR)
8
+
9
+ class PDFReport(FPDF):
10
+ def header(self):
11
+ self.set_font('Arial', 'B', 15)
12
+ self.cell(0, 10, 'Cummins Demand Forecasting Report', 0, 1, 'C')
13
+ self.ln(10)
14
+
15
+ def footer(self):
16
+ self.set_y(-15)
17
+ self.set_font('Arial', 'I', 8)
18
+ self.cell(0, 10, 'Page ' + str(self.page_no()), 0, 0, 'C')
19
+
20
+ def generate_forecast_pdf(item_code, forecast_data, location=None):
21
+ pdf = PDFReport()
22
+ pdf.add_page()
23
+
24
+ # Title Info
25
+ pdf.set_font('Arial', 'B', 12)
26
+ pdf.cell(0, 10, f'Item: {item_code}', 0, 1)
27
+ pdf.cell(0, 10, f'Location: {location if location else "All Locations"}', 0, 1)
28
+ pdf.cell(0, 10, f'Date Generated: {datetime.now().strftime("%Y-%m-%d %H:%M")}', 0, 1)
29
+ pdf.ln(10)
30
+
31
+ # Table Header
32
+ pdf.set_font('Arial', 'B', 10)
33
+ pdf.cell(60, 10, 'Date', 1)
34
+ pdf.cell(40, 10, 'Quantity', 1)
35
+ pdf.ln()
36
+
37
+ # Table Data
38
+ pdf.set_font('Arial', '', 10)
39
+ total_qty = 0
40
+ for row in forecast_data:
41
+ pdf.cell(60, 10, str(row['date']), 1)
42
+ pdf.cell(40, 10, str(row['qty']), 1)
43
+ pdf.ln()
44
+ total_qty += row['qty']
45
+
46
+ pdf.ln(5)
47
+ pdf.set_font('Arial', 'B', 10)
48
+ pdf.cell(60, 10, 'Total Forecasted Demand:', 0)
49
+ pdf.cell(40, 10, str(total_qty), 0)
50
+
51
+ filename = f"forecast_{item_code}_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf"
52
+ filepath = os.path.join(REPORT_DIR, filename)
53
+ pdf.output(filepath)
54
+
55
+ return filename
56
+
57
+ def generate_general_pdf(title, content):
58
+ pdf = PDFReport()
59
+ pdf.add_page()
60
+
61
+ pdf.set_font('Arial', 'B', 12)
62
+ pdf.cell(0, 10, title, 0, 1)
63
+ pdf.ln(5)
64
+
65
+ pdf.set_font('Arial', '', 10)
66
+ pdf.multi_cell(0, 10, content)
67
+
68
+ filename = f"report_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf"
69
+ filepath = os.path.join(REPORT_DIR, filename)
70
+ pdf.output(filepath)
71
+
72
+ return filename
data/company_context.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Cummins Inc. is a global power leader that designs, manufactures, distributes, and services a broad portfolio of power solutions.
2
+ The company’s products range from diesel, natural gas, electric and hybrid powertrains and powertrain-related components including filtration, aftertreatment, turbochargers, fuel systems, controls systems, air handling systems, automated transmissions, electric power generation systems, batteries, electrified power systems, hydrogen generation and fuel cell products.
3
+
4
+ Key Business Segments:
5
+ 1. Engine Segment: Manufactures and markets a complete line of diesel and natural gas-powered engines for on-highway and off-highway use.
6
+ 2. Distribution Segment: Provides parts, service, and support to customers globally.
7
+ 3. Components Segment: Supplies filtration products (Fleetguard), turbochargers (Holset), and aftertreatment systems.
8
+ 4. Power Systems Segment: Provides power generation systems, standby generators, and prime power solutions.
9
+ 5. Accelera (New Power): Focuses on zero-emissions technologies like hydrogen fuel cells and battery electric systems.
10
+
11
+ Our goal is to power a more prosperous world through three global priorities:
12
+ - Addressing climate change and air emissions.
13
+ - Using natural resources sustainably.
14
+ - Improving communities.
15
+
16
+ This chatbot is the BMS AI Assistant, designed to assist with Demand Forecasting for the Components Segment, specifically for filtration products like BMS0015 and BMS0042.
data/demand_history.csv ADDED
The diff for this file is too large to render. See raw diff
 
data/inventory.csv ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ item_code,warehouse_code,warehouse_name,region,on_hand,allocated,available,last_updated
2
+ BMS0001,EU-01,Europe - Daventry,EU,161,4,120,2025-11-29
3
+ BMS0001,APAC-02,Asia Pacific - Shanghai,APAC,301,93,237,2025-11-29
4
+ BMS0001,NA-02,North America - Memphis,NA,742,57,720,2025-11-29
5
+ BMS0002,NA-01,North America - Columbus,NA,244,31,161,2025-11-29
6
+ BMS0002,NA-02,North America - Memphis,NA,374,86,287,2025-11-29
7
+ BMS0002,EU-02,Europe - Rotterdam,EU,241,58,238,2025-11-29
8
+ BMS0002,APAC-01,Asia Pacific - Singapore,APAC,647,54,552,2025-11-29
9
+ BMS0003,NA-02,North America - Memphis,NA,52,0,12,2025-11-29
10
+ BMS0003,APAC-02,Asia Pacific - Shanghai,APAC,76,56,66,2025-11-29
11
+ BMS0004,APAC-02,Asia Pacific - Shanghai,APAC,440,24,424,2025-11-29
12
+ BMS0004,EU-01,Europe - Daventry,EU,609,47,526,2025-11-29
13
+ BMS0004,APAC-01,Asia Pacific - Singapore,APAC,122,50,70,2025-11-29
14
+ BMS0004,NA-02,North America - Memphis,NA,129,9,85,2025-11-29
15
+ BMS0005,EU-02,Europe - Rotterdam,EU,756,48,730,2025-11-29
16
+ BMS0005,APAC-02,Asia Pacific - Shanghai,APAC,508,36,446,2025-11-29
17
+ BMS0005,EU-01,Europe - Daventry,EU,52,15,11,2025-11-29
18
+ BMS0005,APAC-01,Asia Pacific - Singapore,APAC,16,6,3,2025-11-29
19
+ BMS0006,NA-02,North America - Memphis,NA,683,90,677,2025-11-29
20
+ BMS0006,APAC-01,Asia Pacific - Singapore,APAC,130,49,50,2025-11-29
21
+ BMS0006,EU-02,Europe - Rotterdam,EU,523,58,509,2025-11-29
22
+ BMS0007,EU-02,Europe - Rotterdam,EU,341,28,244,2025-11-29
23
+ BMS0007,EU-01,Europe - Daventry,EU,68,68,16,2025-11-29
24
+ BMS0007,APAC-01,Asia Pacific - Singapore,APAC,339,69,298,2025-11-29
25
+ BMS0007,NA-01,North America - Columbus,NA,186,23,122,2025-11-29
26
+ BMS0008,APAC-02,Asia Pacific - Shanghai,APAC,126,57,106,2025-11-29
27
+ BMS0008,EU-02,Europe - Rotterdam,EU,473,34,420,2025-11-29
28
+ BMS0008,APAC-01,Asia Pacific - Singapore,APAC,59,0,44,2025-11-29
29
+ BMS0008,EU-01,Europe - Daventry,EU,301,11,301,2025-11-29
30
+ BMS0009,APAC-01,Asia Pacific - Singapore,APAC,540,57,537,2025-11-29
31
+ BMS0009,APAC-02,Asia Pacific - Shanghai,APAC,241,47,230,2025-11-29
32
+ BMS0009,NA-02,North America - Memphis,NA,488,11,413,2025-11-29
33
+ BMS0010,EU-01,Europe - Daventry,EU,346,1,344,2025-11-29
34
+ BMS0010,APAC-01,Asia Pacific - Singapore,APAC,618,61,612,2025-11-29
35
+ BMS0010,EU-02,Europe - Rotterdam,EU,437,34,353,2025-11-29
36
+ BMS0010,NA-02,North America - Memphis,NA,264,6,176,2025-11-29
37
+ BMS0011,APAC-02,Asia Pacific - Shanghai,APAC,696,41,667,2025-11-29
38
+ BMS0011,NA-01,North America - Columbus,NA,294,53,281,2025-11-29
39
+ BMS0011,NA-02,North America - Memphis,NA,99,41,57,2025-11-29
40
+ BMS0012,EU-01,Europe - Daventry,EU,30,25,26,2025-11-29
41
+ BMS0012,NA-02,North America - Memphis,NA,449,71,363,2025-11-29
42
+ BMS0012,APAC-02,Asia Pacific - Shanghai,APAC,744,23,738,2025-11-29
43
+ BMS0012,EU-02,Europe - Rotterdam,EU,116,46,46,2025-11-29
44
+ BMS0013,NA-01,North America - Columbus,NA,30,28,14,2025-11-29
45
+ BMS0013,NA-02,North America - Memphis,NA,667,29,591,2025-11-29
46
+ BMS0014,NA-02,North America - Memphis,NA,786,55,705,2025-11-29
47
+ BMS0014,NA-01,North America - Columbus,NA,635,41,554,2025-11-29
48
+ BMS0014,APAC-01,Asia Pacific - Singapore,APAC,703,15,658,2025-11-29
49
+ BMS0015,NA-02,North America - Memphis,NA,269,41,171,2025-11-29
50
+ BMS0015,APAC-02,Asia Pacific - Shanghai,APAC,56,53,4,2025-11-29
51
+ BMS0016,APAC-01,Asia Pacific - Singapore,APAC,491,100,482,2025-11-29
52
+ BMS0016,EU-02,Europe - Rotterdam,EU,759,27,697,2025-11-29
53
+ BMS0016,NA-01,North America - Columbus,NA,785,49,736,2025-11-29
54
+ BMS0016,EU-01,Europe - Daventry,EU,478,1,394,2025-11-29
55
+ BMS0017,NA-01,North America - Columbus,NA,54,21,9,2025-11-29
56
+ BMS0017,EU-02,Europe - Rotterdam,EU,61,45,35,2025-11-29
57
+ BMS0017,APAC-02,Asia Pacific - Shanghai,APAC,296,3,251,2025-11-29
58
+ BMS0017,NA-02,North America - Memphis,NA,97,11,28,2025-11-29
59
+ BMS0018,NA-02,North America - Memphis,NA,747,35,745,2025-11-29
60
+ BMS0018,APAC-01,Asia Pacific - Singapore,APAC,714,95,702,2025-11-29
61
+ BMS0019,EU-01,Europe - Daventry,EU,635,2,601,2025-11-29
62
+ BMS0019,APAC-02,Asia Pacific - Shanghai,APAC,206,66,134,2025-11-29
63
+ BMS0019,NA-01,North America - Columbus,NA,772,24,731,2025-11-29
64
+ BMS0019,NA-02,North America - Memphis,NA,230,12,172,2025-11-29
65
+ BMS0020,APAC-02,Asia Pacific - Shanghai,APAC,297,55,284,2025-11-29
66
+ BMS0020,NA-02,North America - Memphis,NA,359,85,314,2025-11-29
67
+ BMS0021,NA-02,North America - Memphis,NA,666,90,656,2025-11-29
68
+ BMS0021,EU-01,Europe - Daventry,EU,68,31,35,2025-11-29
69
+ BMS0022,EU-01,Europe - Daventry,EU,106,17,53,2025-11-29
70
+ BMS0022,APAC-01,Asia Pacific - Singapore,APAC,749,70,677,2025-11-29
71
+ BMS0023,EU-02,Europe - Rotterdam,EU,552,89,524,2025-11-29
72
+ BMS0023,EU-01,Europe - Daventry,EU,335,81,313,2025-11-29
73
+ BMS0024,NA-02,North America - Memphis,NA,743,83,665,2025-11-29
74
+ BMS0024,APAC-01,Asia Pacific - Singapore,APAC,419,100,355,2025-11-29
75
+ BMS0024,APAC-02,Asia Pacific - Shanghai,APAC,559,41,507,2025-11-29
76
+ BMS0025,APAC-02,Asia Pacific - Shanghai,APAC,625,34,584,2025-11-29
77
+ BMS0025,EU-02,Europe - Rotterdam,EU,773,51,708,2025-11-29
78
+ BMS0026,EU-01,Europe - Daventry,EU,220,10,141,2025-11-29
79
+ BMS0026,EU-02,Europe - Rotterdam,EU,383,62,311,2025-11-29
80
+ BMS0026,NA-01,North America - Columbus,NA,681,75,627,2025-11-29
81
+ BMS0026,APAC-02,Asia Pacific - Shanghai,APAC,338,72,325,2025-11-29
82
+ BMS0027,APAC-02,Asia Pacific - Shanghai,APAC,608,95,576,2025-11-29
83
+ BMS0027,NA-02,North America - Memphis,NA,262,82,163,2025-11-29
84
+ BMS0028,EU-01,Europe - Daventry,EU,474,83,386,2025-11-29
85
+ BMS0028,APAC-01,Asia Pacific - Singapore,APAC,566,39,524,2025-11-29
86
+ BMS0028,NA-02,North America - Memphis,NA,465,64,432,2025-11-29
87
+ BMS0028,NA-01,North America - Columbus,NA,256,76,173,2025-11-29
88
+ BMS0029,NA-01,North America - Columbus,NA,491,60,477,2025-11-29
89
+ BMS0029,NA-02,North America - Memphis,NA,317,83,268,2025-11-29
90
+ BMS0029,APAC-01,Asia Pacific - Singapore,APAC,337,87,333,2025-11-29
91
+ BMS0029,APAC-02,Asia Pacific - Shanghai,APAC,669,86,650,2025-11-29
92
+ BMS0030,EU-02,Europe - Rotterdam,EU,519,31,463,2025-11-29
93
+ BMS0030,NA-01,North America - Columbus,NA,418,78,410,2025-11-29
94
+ BMS0030,APAC-01,Asia Pacific - Singapore,APAC,32,32,28,2025-11-29
95
+ BMS0030,APAC-02,Asia Pacific - Shanghai,APAC,559,91,483,2025-11-29
96
+ BMS0031,NA-01,North America - Columbus,NA,56,7,49,2025-11-29
97
+ BMS0031,NA-02,North America - Memphis,NA,45,18,16,2025-11-29
98
+ BMS0031,EU-02,Europe - Rotterdam,EU,3,2,1,2025-11-29
99
+ BMS0031,EU-01,Europe - Daventry,EU,714,100,682,2025-11-29
100
+ BMS0032,EU-01,Europe - Daventry,EU,357,51,297,2025-11-29
101
+ BMS0032,NA-02,North America - Memphis,NA,137,11,49,2025-11-29
102
+ BMS0032,APAC-01,Asia Pacific - Singapore,APAC,65,42,56,2025-11-29
103
+ BMS0033,NA-01,North America - Columbus,NA,279,46,187,2025-11-29
104
+ BMS0033,APAC-01,Asia Pacific - Singapore,APAC,702,86,602,2025-11-29
105
+ BMS0034,EU-02,Europe - Rotterdam,EU,774,67,693,2025-11-29
106
+ BMS0034,EU-01,Europe - Daventry,EU,135,9,83,2025-11-29
107
+ BMS0035,EU-01,Europe - Daventry,EU,444,22,357,2025-11-29
108
+ BMS0035,NA-01,North America - Columbus,NA,11,0,11,2025-11-29
109
+ BMS0035,APAC-02,Asia Pacific - Shanghai,APAC,545,65,476,2025-11-29
110
+ BMS0036,APAC-01,Asia Pacific - Singapore,APAC,762,100,749,2025-11-29
111
+ BMS0036,EU-02,Europe - Rotterdam,EU,315,30,257,2025-11-29
112
+ BMS0036,APAC-02,Asia Pacific - Shanghai,APAC,455,21,435,2025-11-29
113
+ BMS0036,NA-02,North America - Memphis,NA,183,64,136,2025-11-29
114
+ BMS0037,EU-02,Europe - Rotterdam,EU,80,35,8,2025-11-29
115
+ BMS0037,APAC-01,Asia Pacific - Singapore,APAC,237,45,201,2025-11-29
116
+ BMS0037,NA-02,North America - Memphis,NA,549,71,540,2025-11-29
117
+ BMS0038,NA-01,North America - Columbus,NA,474,53,383,2025-11-29
118
+ BMS0038,EU-02,Europe - Rotterdam,EU,73,5,43,2025-11-29
119
+ BMS0038,NA-02,North America - Memphis,NA,458,60,401,2025-11-29
120
+ BMS0038,APAC-02,Asia Pacific - Shanghai,APAC,252,11,240,2025-11-29
121
+ BMS0039,NA-02,North America - Memphis,NA,706,24,606,2025-11-29
122
+ BMS0039,EU-01,Europe - Daventry,EU,465,84,425,2025-11-29
123
+ BMS0040,NA-02,North America - Memphis,NA,321,14,233,2025-11-29
124
+ BMS0040,APAC-02,Asia Pacific - Shanghai,APAC,355,85,347,2025-11-29
125
+ BMS0040,EU-01,Europe - Daventry,EU,640,37,567,2025-11-29
126
+ BMS0040,EU-02,Europe - Rotterdam,EU,280,5,195,2025-11-29
127
+ BMS0041,EU-02,Europe - Rotterdam,EU,441,60,424,2025-11-29
128
+ BMS0041,EU-01,Europe - Daventry,EU,232,75,174,2025-11-29
129
+ BMS0042,APAC-01,Asia Pacific - Singapore,APAC,537,64,519,2025-11-29
130
+ BMS0042,APAC-02,Asia Pacific - Shanghai,APAC,98,96,3,2025-11-29
131
+ BMS0042,NA-01,North America - Columbus,NA,768,6,712,2025-11-29
132
+ BMS0042,EU-02,Europe - Rotterdam,EU,386,98,289,2025-11-29
133
+ BMS0043,NA-01,North America - Columbus,NA,548,84,467,2025-11-29
134
+ BMS0043,EU-01,Europe - Daventry,EU,703,87,644,2025-11-29
135
+ BMS0043,APAC-01,Asia Pacific - Singapore,APAC,788,5,756,2025-11-29
136
+ BMS0043,NA-02,North America - Memphis,NA,147,47,119,2025-11-29
137
+ BMS0044,EU-02,Europe - Rotterdam,EU,733,52,672,2025-11-29
138
+ BMS0044,APAC-02,Asia Pacific - Shanghai,APAC,732,31,716,2025-11-29
139
+ BMS0044,APAC-01,Asia Pacific - Singapore,APAC,382,34,307,2025-11-29
140
+ BMS0044,EU-01,Europe - Daventry,EU,401,48,398,2025-11-29
141
+ BMS0045,NA-01,North America - Columbus,NA,219,96,156,2025-11-29
142
+ BMS0045,NA-02,North America - Memphis,NA,130,26,60,2025-11-29
143
+ BMS0045,EU-02,Europe - Rotterdam,EU,29,11,15,2025-11-29
144
+ BMS0046,NA-02,North America - Memphis,NA,412,94,357,2025-11-29
145
+ BMS0046,NA-01,North America - Columbus,NA,184,77,135,2025-11-29
146
+ BMS0047,EU-02,Europe - Rotterdam,EU,234,33,213,2025-11-29
147
+ BMS0047,APAC-01,Asia Pacific - Singapore,APAC,536,72,529,2025-11-29
148
+ BMS0047,NA-02,North America - Memphis,NA,669,33,648,2025-11-29
149
+ BMS0047,APAC-02,Asia Pacific - Shanghai,APAC,406,69,365,2025-11-29
150
+ BMS0048,APAC-02,Asia Pacific - Shanghai,APAC,3,3,1,2025-11-29
151
+ BMS0048,EU-01,Europe - Daventry,EU,518,77,441,2025-11-29
152
+ BMS0048,APAC-01,Asia Pacific - Singapore,APAC,244,18,235,2025-11-29
153
+ BMS0048,NA-02,North America - Memphis,NA,638,9,553,2025-11-29
154
+ BMS0049,NA-02,North America - Memphis,NA,304,16,292,2025-11-29
155
+ BMS0049,APAC-02,Asia Pacific - Shanghai,APAC,489,77,448,2025-11-29
156
+ BMS0049,NA-01,North America - Columbus,NA,215,38,127,2025-11-29
157
+ BMS0049,APAC-01,Asia Pacific - Singapore,APAC,544,99,500,2025-11-29
158
+ BMS0050,EU-01,Europe - Daventry,EU,290,39,237,2025-11-29
159
+ BMS0050,APAC-02,Asia Pacific - Shanghai,APAC,172,33,169,2025-11-29
160
+ BMS0050,NA-01,North America - Columbus,NA,794,39,707,2025-11-29
161
+ BMS0051,EU-01,Europe - Daventry,EU,93,54,1,2025-11-29
162
+ BMS0051,NA-01,North America - Columbus,NA,675,91,583,2025-11-29
163
+ BMS0051,EU-02,Europe - Rotterdam,EU,769,49,671,2025-11-29
164
+ BMS0051,APAC-01,Asia Pacific - Singapore,APAC,771,36,759,2025-11-29
165
+ BMS0052,NA-01,North America - Columbus,NA,226,28,170,2025-11-29
166
+ BMS0052,APAC-02,Asia Pacific - Shanghai,APAC,600,2,568,2025-11-29
167
+ BMS0052,APAC-01,Asia Pacific - Singapore,APAC,451,34,435,2025-11-29
168
+ BMS0053,NA-02,North America - Memphis,NA,435,86,385,2025-11-29
169
+ BMS0053,NA-01,North America - Columbus,NA,21,11,0,2025-11-29
170
+ BMS0054,NA-01,North America - Columbus,NA,607,0,511,2025-11-29
171
+ BMS0054,APAC-01,Asia Pacific - Singapore,APAC,134,92,63,2025-11-29
172
+ BMS0054,EU-01,Europe - Daventry,EU,682,74,670,2025-11-29
173
+ BMS0054,NA-02,North America - Memphis,NA,642,63,636,2025-11-29
174
+ BMS0055,APAC-01,Asia Pacific - Singapore,APAC,144,35,125,2025-11-29
175
+ BMS0055,APAC-02,Asia Pacific - Shanghai,APAC,63,61,21,2025-11-29
176
+ BMS0055,NA-01,North America - Columbus,NA,240,2,144,2025-11-29
177
+ BMS0055,NA-02,North America - Memphis,NA,574,88,518,2025-11-29
178
+ BMS0056,NA-02,North America - Memphis,NA,612,54,513,2025-11-29
179
+ BMS0056,APAC-02,Asia Pacific - Shanghai,APAC,400,56,372,2025-11-29
180
+ BMS0056,EU-02,Europe - Rotterdam,EU,285,79,245,2025-11-29
181
+ BMS0057,APAC-02,Asia Pacific - Shanghai,APAC,754,41,663,2025-11-29
182
+ BMS0057,EU-02,Europe - Rotterdam,EU,36,2,3,2025-11-29
183
+ BMS0057,NA-01,North America - Columbus,NA,380,100,309,2025-11-29
184
+ BMS0057,APAC-01,Asia Pacific - Singapore,APAC,330,97,296,2025-11-29
185
+ BMS0058,NA-01,North America - Columbus,NA,69,55,6,2025-11-29
186
+ BMS0058,EU-01,Europe - Daventry,EU,598,21,584,2025-11-29
187
+ BMS0058,EU-02,Europe - Rotterdam,EU,146,56,138,2025-11-29
188
+ BMS0058,NA-02,North America - Memphis,NA,736,77,696,2025-11-29
189
+ BMS0059,EU-02,Europe - Rotterdam,EU,494,15,429,2025-11-29
190
+ BMS0059,NA-01,North America - Columbus,NA,429,60,422,2025-11-29
191
+ BMS0060,APAC-01,Asia Pacific - Singapore,APAC,730,95,670,2025-11-29
192
+ BMS0060,EU-02,Europe - Rotterdam,EU,143,3,135,2025-11-29
193
+ BMS0060,NA-02,North America - Memphis,NA,790,55,758,2025-11-29
194
+ BMS0060,EU-01,Europe - Daventry,EU,554,83,514,2025-11-29
195
+ BMS0061,EU-02,Europe - Rotterdam,EU,224,17,139,2025-11-29
196
+ BMS0061,NA-01,North America - Columbus,NA,333,70,322,2025-11-29
197
+ BMS0061,NA-02,North America - Memphis,NA,717,74,627,2025-11-29
198
+ BMS0062,NA-02,North America - Memphis,NA,233,2,229,2025-11-29
199
+ BMS0062,EU-01,Europe - Daventry,EU,310,87,280,2025-11-29
200
+ BMS0062,NA-01,North America - Columbus,NA,393,18,346,2025-11-29
201
+ BMS0063,EU-02,Europe - Rotterdam,EU,341,31,246,2025-11-29
202
+ BMS0063,APAC-01,Asia Pacific - Singapore,APAC,658,93,581,2025-11-29
203
+ BMS0063,NA-01,North America - Columbus,NA,692,30,692,2025-11-29
204
+ BMS0063,EU-01,Europe - Daventry,EU,351,71,251,2025-11-29
205
+ BMS0064,APAC-01,Asia Pacific - Singapore,APAC,709,48,688,2025-11-29
206
+ BMS0064,EU-02,Europe - Rotterdam,EU,288,0,253,2025-11-29
207
+ BMS0065,EU-02,Europe - Rotterdam,EU,65,4,10,2025-11-29
208
+ BMS0065,NA-01,North America - Columbus,NA,702,89,694,2025-11-29
209
+ BMS0065,APAC-01,Asia Pacific - Singapore,APAC,586,44,491,2025-11-29
210
+ BMS0066,NA-01,North America - Columbus,NA,627,25,536,2025-11-29
211
+ BMS0066,EU-02,Europe - Rotterdam,EU,152,73,63,2025-11-29
212
+ BMS0066,APAC-02,Asia Pacific - Shanghai,APAC,554,55,513,2025-11-29
213
+ BMS0066,EU-01,Europe - Daventry,EU,197,54,148,2025-11-29
214
+ BMS0067,EU-01,Europe - Daventry,EU,17,2,0,2025-11-29
215
+ BMS0067,APAC-02,Asia Pacific - Shanghai,APAC,73,71,12,2025-11-29
216
+ BMS0068,EU-01,Europe - Daventry,EU,308,96,217,2025-11-29
217
+ BMS0068,NA-01,North America - Columbus,NA,207,49,125,2025-11-29
218
+ BMS0068,NA-02,North America - Memphis,NA,76,22,33,2025-11-29
219
+ BMS0068,EU-02,Europe - Rotterdam,EU,67,36,46,2025-11-29
220
+ BMS0069,APAC-02,Asia Pacific - Shanghai,APAC,617,86,607,2025-11-29
221
+ BMS0069,EU-02,Europe - Rotterdam,EU,133,39,125,2025-11-29
222
+ BMS0069,NA-01,North America - Columbus,NA,335,91,315,2025-11-29
223
+ BMS0069,EU-01,Europe - Daventry,EU,68,19,58,2025-11-29
224
+ BMS0070,APAC-02,Asia Pacific - Shanghai,APAC,559,58,533,2025-11-29
225
+ BMS0070,APAC-01,Asia Pacific - Singapore,APAC,17,1,5,2025-11-29
226
+ BMS0070,NA-02,North America - Memphis,NA,39,15,29,2025-11-29
227
+ BMS0070,NA-01,North America - Columbus,NA,517,75,461,2025-11-29
228
+ BMS0071,APAC-02,Asia Pacific - Shanghai,APAC,479,95,420,2025-11-29
229
+ BMS0071,EU-01,Europe - Daventry,EU,376,55,282,2025-11-29
230
+ BMS0071,EU-02,Europe - Rotterdam,EU,761,88,681,2025-11-29
231
+ BMS0071,NA-01,North America - Columbus,NA,303,82,290,2025-11-29
232
+ BMS0072,APAC-01,Asia Pacific - Singapore,APAC,315,47,298,2025-11-29
233
+ BMS0072,APAC-02,Asia Pacific - Shanghai,APAC,32,30,24,2025-11-29
234
+ BMS0073,APAC-01,Asia Pacific - Singapore,APAC,197,63,148,2025-11-29
235
+ BMS0073,EU-01,Europe - Daventry,EU,268,74,230,2025-11-29
236
+ BMS0073,NA-01,North America - Columbus,NA,710,10,686,2025-11-29
237
+ BMS0074,NA-01,North America - Columbus,NA,524,32,431,2025-11-29
238
+ BMS0074,APAC-01,Asia Pacific - Singapore,APAC,327,46,308,2025-11-29
239
+ BMS0075,NA-02,North America - Memphis,NA,636,90,604,2025-11-29
240
+ BMS0075,EU-01,Europe - Daventry,EU,128,21,63,2025-11-29
241
+ BMS0075,APAC-01,Asia Pacific - Singapore,APAC,349,9,316,2025-11-29
242
+ BMS0075,APAC-02,Asia Pacific - Shanghai,APAC,93,30,43,2025-11-29
243
+ BMS0076,NA-02,North America - Memphis,NA,540,99,485,2025-11-29
244
+ BMS0076,APAC-02,Asia Pacific - Shanghai,APAC,111,30,84,2025-11-29
245
+ BMS0076,EU-01,Europe - Daventry,EU,752,24,669,2025-11-29
246
+ BMS0076,APAC-01,Asia Pacific - Singapore,APAC,218,1,174,2025-11-29
247
+ BMS0077,NA-01,North America - Columbus,NA,79,15,48,2025-11-29
248
+ BMS0077,NA-02,North America - Memphis,NA,504,17,478,2025-11-29
249
+ BMS0077,APAC-02,Asia Pacific - Shanghai,APAC,713,53,653,2025-11-29
250
+ BMS0078,APAC-02,Asia Pacific - Shanghai,APAC,184,95,169,2025-11-29
251
+ BMS0078,APAC-01,Asia Pacific - Singapore,APAC,216,94,169,2025-11-29
252
+ BMS0078,NA-01,North America - Columbus,NA,743,80,721,2025-11-29
253
+ BMS0078,EU-01,Europe - Daventry,EU,757,19,748,2025-11-29
254
+ BMS0079,NA-01,North America - Columbus,NA,528,74,525,2025-11-29
255
+ BMS0079,APAC-02,Asia Pacific - Shanghai,APAC,586,70,559,2025-11-29
256
+ BMS0080,APAC-02,Asia Pacific - Shanghai,APAC,583,27,555,2025-11-29
257
+ BMS0080,NA-01,North America - Columbus,NA,211,5,135,2025-11-29
258
+ BMS0080,EU-02,Europe - Rotterdam,EU,668,8,644,2025-11-29
259
+ BMS0080,EU-01,Europe - Daventry,EU,528,83,451,2025-11-29
260
+ BMS0081,APAC-02,Asia Pacific - Shanghai,APAC,575,54,573,2025-11-29
261
+ BMS0081,APAC-01,Asia Pacific - Singapore,APAC,165,2,68,2025-11-29
262
+ BMS0082,NA-02,North America - Memphis,NA,679,93,615,2025-11-29
263
+ BMS0082,APAC-01,Asia Pacific - Singapore,APAC,651,15,582,2025-11-29
264
+ BMS0082,EU-02,Europe - Rotterdam,EU,122,34,92,2025-11-29
265
+ BMS0082,EU-01,Europe - Daventry,EU,208,97,168,2025-11-29
266
+ BMS0083,APAC-01,Asia Pacific - Singapore,APAC,254,47,190,2025-11-29
267
+ BMS0083,EU-02,Europe - Rotterdam,EU,735,71,734,2025-11-29
268
+ BMS0083,NA-02,North America - Memphis,NA,371,69,304,2025-11-29
269
+ BMS0083,EU-01,Europe - Daventry,EU,792,57,730,2025-11-29
270
+ BMS0084,APAC-01,Asia Pacific - Singapore,APAC,417,71,354,2025-11-29
271
+ BMS0084,EU-02,Europe - Rotterdam,EU,582,6,560,2025-11-29
272
+ BMS0084,NA-01,North America - Columbus,NA,286,36,233,2025-11-29
273
+ BMS0084,NA-02,North America - Memphis,NA,267,34,247,2025-11-29
274
+ BMS0085,APAC-02,Asia Pacific - Shanghai,APAC,588,67,496,2025-11-29
275
+ BMS0085,EU-01,Europe - Daventry,EU,648,92,600,2025-11-29
276
+ BMS0086,APAC-01,Asia Pacific - Singapore,APAC,707,92,608,2025-11-29
277
+ BMS0086,EU-01,Europe - Daventry,EU,597,23,510,2025-11-29
278
+ BMS0086,NA-01,North America - Columbus,NA,791,60,788,2025-11-29
279
+ BMS0087,APAC-01,Asia Pacific - Singapore,APAC,496,47,464,2025-11-29
280
+ BMS0087,NA-01,North America - Columbus,NA,148,19,67,2025-11-29
281
+ BMS0088,APAC-01,Asia Pacific - Singapore,APAC,92,38,33,2025-11-29
282
+ BMS0088,APAC-02,Asia Pacific - Shanghai,APAC,396,69,323,2025-11-29
283
+ BMS0088,NA-01,North America - Columbus,NA,563,99,537,2025-11-29
284
+ BMS0089,NA-02,North America - Memphis,NA,343,79,293,2025-11-29
285
+ BMS0089,APAC-01,Asia Pacific - Singapore,APAC,365,97,300,2025-11-29
286
+ BMS0089,NA-01,North America - Columbus,NA,384,53,298,2025-11-29
287
+ BMS0090,APAC-02,Asia Pacific - Shanghai,APAC,798,31,764,2025-11-29
288
+ BMS0090,NA-01,North America - Columbus,NA,551,32,508,2025-11-29
289
+ BMS0090,EU-01,Europe - Daventry,EU,283,41,253,2025-11-29
290
+ BMS0090,APAC-01,Asia Pacific - Singapore,APAC,431,58,359,2025-11-29
291
+ BMS0091,APAC-02,Asia Pacific - Shanghai,APAC,426,24,399,2025-11-29
292
+ BMS0091,NA-02,North America - Memphis,NA,266,44,179,2025-11-29
293
+ BMS0092,NA-01,North America - Columbus,NA,73,60,28,2025-11-29
294
+ BMS0092,APAC-01,Asia Pacific - Singapore,APAC,607,48,555,2025-11-29
295
+ BMS0092,APAC-02,Asia Pacific - Shanghai,APAC,88,37,49,2025-11-29
296
+ BMS0093,EU-02,Europe - Rotterdam,EU,683,34,609,2025-11-29
297
+ BMS0093,APAC-02,Asia Pacific - Shanghai,APAC,60,33,16,2025-11-29
298
+ BMS0093,EU-01,Europe - Daventry,EU,621,91,577,2025-11-29
299
+ BMS0093,APAC-01,Asia Pacific - Singapore,APAC,481,15,475,2025-11-29
300
+ BMS0094,NA-01,North America - Columbus,NA,98,32,97,2025-11-29
301
+ BMS0094,EU-02,Europe - Rotterdam,EU,180,21,96,2025-11-29
302
+ BMS0094,APAC-02,Asia Pacific - Shanghai,APAC,219,81,176,2025-11-29
303
+ BMS0095,EU-02,Europe - Rotterdam,EU,533,42,488,2025-11-29
304
+ BMS0095,NA-01,North America - Columbus,NA,177,22,80,2025-11-29
305
+ BMS0095,APAC-02,Asia Pacific - Shanghai,APAC,601,100,555,2025-11-29
306
+ BMS0095,NA-02,North America - Memphis,NA,23,19,2,2025-11-29
307
+ BMS0096,EU-01,Europe - Daventry,EU,19,8,2,2025-11-29
308
+ BMS0096,NA-02,North America - Memphis,NA,526,36,458,2025-11-29
309
+ BMS0096,EU-02,Europe - Rotterdam,EU,673,89,582,2025-11-29
310
+ BMS0097,EU-02,Europe - Rotterdam,EU,291,41,235,2025-11-29
311
+ BMS0097,NA-01,North America - Columbus,NA,765,40,709,2025-11-29
312
+ BMS0097,APAC-01,Asia Pacific - Singapore,APAC,412,66,396,2025-11-29
313
+ BMS0097,NA-02,North America - Memphis,NA,442,83,415,2025-11-29
314
+ BMS0098,APAC-01,Asia Pacific - Singapore,APAC,519,74,435,2025-11-29
315
+ BMS0098,NA-01,North America - Columbus,NA,487,29,405,2025-11-29
316
+ BMS0098,APAC-02,Asia Pacific - Shanghai,APAC,48,14,21,2025-11-29
317
+ BMS0099,APAC-01,Asia Pacific - Singapore,APAC,479,35,425,2025-11-29
318
+ BMS0099,NA-01,North America - Columbus,NA,448,32,421,2025-11-29
319
+ BMS0099,EU-02,Europe - Rotterdam,EU,199,38,199,2025-11-29
320
+ BMS0100,APAC-02,Asia Pacific - Shanghai,APAC,346,43,343,2025-11-29
321
+ BMS0100,EU-02,Europe - Rotterdam,EU,112,88,26,2025-11-29
322
+ BMS0100,NA-01,North America - Columbus,NA,204,26,180,2025-11-29
323
+ BMS0100,NA-02,North America - Memphis,NA,793,37,764,2025-11-29
data/items.csv ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ item_code,description,category,uom,list_price,cost_price,reorder_point,reorder_qty,supplier_id,active,created_date
2
+ BMS0001,Belt - Type A - Model 859,Belts,Each,51.13,30.4,76,479,SUP002,Y,2024-01-01
3
+ BMS0002,Switche - Type D - Model 132,Switches,Each,22.09,23.15,179,213,SUP009,Y,2024-01-01
4
+ BMS0003,Gasket - Type D - Model 325,Gaskets,Each,24.62,12.23,51,281,SUP007,Y,2024-01-01
5
+ BMS0004,Seal - Type C - Model 259,Seals,Each,22.34,33.77,76,247,SUP007,Y,2024-01-01
6
+ BMS0005,Belt - Type C - Model 967,Belts,Each,57.68,33.25,61,435,SUP009,Y,2024-01-01
7
+ BMS0006,Belt - Type D - Model 180,Belts,Each,77.44,72.23,142,495,SUP004,Y,2024-01-01
8
+ BMS0007,Belt - Type A - Model 777,Belts,Each,46.65,34.97,70,319,SUP002,Y,2024-01-01
9
+ BMS0008,Sensor - Type C - Model 564,Sensors,Each,143.53,68.23,144,381,SUP004,Y,2024-01-01
10
+ BMS0009,Bearing - Type A - Model 723,Bearings,Each,106.2,64.47,112,283,SUP008,Y,2024-01-01
11
+ BMS0010,Sensor - Type C - Model 755,Sensors,Each,151.67,51.82,133,228,SUP004,Y,2024-01-01
12
+ BMS0011,Filter - Type C - Model 510,Filters,Each,33.74,19.65,195,361,SUP004,Y,2024-01-01
13
+ BMS0012,Valve - Type D - Model 758,Valves,Each,101.53,48.81,113,487,SUP009,Y,2024-01-01
14
+ BMS0013,Bearing - Type D - Model 697,Bearings,Each,77.93,37.08,85,460,SUP008,Y,2024-01-01
15
+ BMS0014,Belt - Type A - Model 981,Belts,Each,35.42,58.29,158,232,SUP007,Y,2024-01-01
16
+ BMS0015,Sensor - Type D - Model 641,Sensors,Each,83.97,89.51,52,258,SUP009,Y,2024-01-01
17
+ BMS0016,Bearing - Type C - Model 214,Bearings,Each,65.22,31.76,50,334,SUP009,Y,2024-01-01
18
+ BMS0017,Hose - Type A - Model 991,Hoses,Each,66.15,57.68,179,301,SUP003,Y,2024-01-01
19
+ BMS0018,Seal - Type B - Model 652,Seals,Each,57.77,39.3,50,365,SUP008,Y,2024-01-01
20
+ BMS0019,Filter - Type A - Model 471,Filters,Each,76.51,51.0,128,322,SUP001,Y,2024-01-01
21
+ BMS0020,Gasket - Type A - Model 187,Gaskets,Each,35.08,26.59,186,264,SUP003,Y,2024-01-01
22
+ BMS0021,Valve - Type B - Model 371,Valves,Each,111.51,84.69,104,476,SUP004,Y,2024-01-01
23
+ BMS0022,Bearing - Type D - Model 787,Bearings,Each,107.99,56.11,182,431,SUP002,Y,2024-01-01
24
+ BMS0023,Gasket - Type B - Model 165,Gaskets,Each,20.51,20.51,108,312,SUP001,Y,2024-01-01
25
+ BMS0024,Belt - Type A - Model 334,Belts,Each,31.4,17.17,134,236,SUP009,Y,2024-01-01
26
+ BMS0025,Gasket - Type C - Model 785,Gaskets,Each,25.96,19.2,196,495,SUP008,Y,2024-01-01
27
+ BMS0026,Gasket - Type D - Model 926,Gaskets,Each,23.06,7.32,160,381,SUP007,Y,2024-01-01
28
+ BMS0027,Sensor - Type D - Model 984,Sensors,Each,158.01,103.09,75,231,SUP007,Y,2024-01-01
29
+ BMS0028,Seal - Type A - Model 354,Seals,Each,21.2,25.86,85,416,SUP003,Y,2024-01-01
30
+ BMS0029,Bearing - Type D - Model 355,Bearings,Each,134.94,24.56,190,250,SUP001,Y,2024-01-01
31
+ BMS0030,Pump - Type A - Model 195,Pumps,Each,472.02,307.94,92,408,SUP008,Y,2024-01-01
32
+ BMS0031,Valve - Type B - Model 985,Valves,Each,93.15,27.16,147,201,SUP007,Y,2024-01-01
33
+ BMS0032,Bearing - Type D - Model 392,Bearings,Each,80.76,101.29,192,449,SUP003,Y,2024-01-01
34
+ BMS0033,Gasket - Type C - Model 322,Gaskets,Each,43.84,20.26,188,231,SUP006,Y,2024-01-01
35
+ BMS0034,Filter - Type A - Model 698,Filters,Each,48.38,55.43,185,280,SUP001,Y,2024-01-01
36
+ BMS0035,Pump - Type A - Model 971,Pumps,Each,190.61,237.42,110,406,SUP002,Y,2024-01-01
37
+ BMS0036,Switche - Type B - Model 692,Switches,Each,61.62,43.59,157,498,SUP010,Y,2024-01-01
38
+ BMS0037,Pump - Type C - Model 367,Pumps,Each,197.62,271.1,111,335,SUP007,Y,2024-01-01
39
+ BMS0038,Hose - Type C - Model 568,Hoses,Each,42.35,52.68,68,204,SUP008,Y,2024-01-01
40
+ BMS0039,Switche - Type A - Model 175,Switches,Each,57.63,37.8,83,378,SUP002,Y,2024-01-01
41
+ BMS0040,Gasket - Type C - Model 391,Gaskets,Each,13.84,27.06,127,470,SUP001,Y,2024-01-01
42
+ BMS0041,Pump - Type C - Model 779,Pumps,Each,159.36,316.12,117,259,SUP002,Y,2024-01-01
43
+ BMS0042,Pump - Type B - Model 378,Pumps,Each,227.07,130.56,137,304,SUP005,Y,2024-01-01
44
+ BMS0043,Pump - Type D - Model 357,Pumps,Each,464.03,307.22,73,416,SUP005,Y,2024-01-01
45
+ BMS0044,Filter - Type A - Model 441,Filters,Each,68.98,41.17,117,282,SUP008,Y,2024-01-01
46
+ BMS0045,Pump - Type D - Model 674,Pumps,Each,123.67,92.92,88,479,SUP001,Y,2024-01-01
47
+ BMS0046,Seal - Type B - Model 540,Seals,Each,18.12,17.93,60,383,SUP004,Y,2024-01-01
48
+ BMS0047,Gasket - Type A - Model 462,Gaskets,Each,36.86,28.41,154,279,SUP004,Y,2024-01-01
49
+ BMS0048,Hose - Type B - Model 522,Hoses,Each,19.91,51.83,135,410,SUP004,Y,2024-01-01
50
+ BMS0049,Bearing - Type B - Model 906,Bearings,Each,114.15,51.28,59,440,SUP004,Y,2024-01-01
51
+ BMS0050,Gasket - Type D - Model 458,Gaskets,Each,19.29,26.04,108,314,SUP001,Y,2024-01-01
52
+ BMS0051,Gasket - Type D - Model 436,Gaskets,Each,18.31,6.65,121,379,SUP009,Y,2024-01-01
53
+ BMS0052,Sensor - Type C - Model 128,Sensors,Each,62.88,136.66,95,497,SUP005,Y,2024-01-01
54
+ BMS0053,Filter - Type A - Model 710,Filters,Each,45.42,45.79,130,423,SUP010,Y,2024-01-01
55
+ BMS0054,Pump - Type A - Model 494,Pumps,Each,461.81,124.84,61,423,SUP001,Y,2024-01-01
56
+ BMS0055,Pump - Type B - Model 472,Pumps,Each,283.89,335.79,134,360,SUP002,Y,2024-01-01
57
+ BMS0056,Bearing - Type C - Model 782,Bearings,Each,79.01,53.01,125,483,SUP003,Y,2024-01-01
58
+ BMS0057,Gasket - Type D - Model 780,Gaskets,Each,42.79,22.88,94,491,SUP005,Y,2024-01-01
59
+ BMS0058,Sensor - Type A - Model 411,Sensors,Each,89.47,75.58,198,364,SUP008,Y,2024-01-01
60
+ BMS0059,Valve - Type D - Model 791,Valves,Each,65.99,70.68,93,243,SUP005,Y,2024-01-01
61
+ BMS0060,Pump - Type C - Model 195,Pumps,Each,431.0,280.82,129,315,SUP004,Y,2024-01-01
62
+ BMS0061,Hose - Type A - Model 147,Hoses,Each,36.85,37.27,68,433,SUP007,Y,2024-01-01
63
+ BMS0062,Switche - Type B - Model 835,Switches,Each,68.74,37.21,112,275,SUP001,Y,2024-01-01
64
+ BMS0063,Belt - Type D - Model 324,Belts,Each,41.71,81.1,182,437,SUP001,Y,2024-01-01
65
+ BMS0064,Pump - Type B - Model 968,Pumps,Each,166.12,109.07,168,471,SUP009,Y,2024-01-01
66
+ BMS0065,Switche - Type C - Model 873,Switches,Each,82.4,43.25,179,418,SUP009,Y,2024-01-01
67
+ BMS0066,Valve - Type B - Model 861,Valves,Each,159.92,68.26,113,341,SUP009,Y,2024-01-01
68
+ BMS0067,Valve - Type B - Model 381,Valves,Each,98.78,95.92,110,339,SUP006,Y,2024-01-01
69
+ BMS0068,Seal - Type A - Model 241,Seals,Each,19.24,20.53,89,309,SUP002,Y,2024-01-01
70
+ BMS0069,Sensor - Type D - Model 438,Sensors,Each,129.1,73.98,102,415,SUP007,Y,2024-01-01
71
+ BMS0070,Switche - Type A - Model 977,Switches,Each,81.65,41.36,172,203,SUP006,Y,2024-01-01
72
+ BMS0071,Bearing - Type D - Model 973,Bearings,Each,137.0,90.71,187,479,SUP010,Y,2024-01-01
73
+ BMS0072,Gasket - Type D - Model 324,Gaskets,Each,18.1,17.77,149,372,SUP007,Y,2024-01-01
74
+ BMS0073,Hose - Type D - Model 230,Hoses,Each,93.52,40.55,150,488,SUP001,Y,2024-01-01
75
+ BMS0074,Belt - Type D - Model 238,Belts,Each,107.35,27.54,116,394,SUP006,Y,2024-01-01
76
+ BMS0075,Gasket - Type D - Model 434,Gaskets,Each,20.49,28.29,121,415,SUP005,Y,2024-01-01
77
+ BMS0076,Belt - Type D - Model 119,Belts,Each,96.16,18.59,139,314,SUP002,Y,2024-01-01
78
+ BMS0077,Filter - Type A - Model 353,Filters,Each,28.96,10.03,89,322,SUP003,Y,2024-01-01
79
+ BMS0078,Valve - Type A - Model 677,Valves,Each,172.42,69.83,115,388,SUP003,Y,2024-01-01
80
+ BMS0079,Switche - Type A - Model 896,Switches,Each,77.35,61.2,77,496,SUP001,Y,2024-01-01
81
+ BMS0080,Bearing - Type D - Model 506,Bearings,Each,142.98,35.25,112,252,SUP005,Y,2024-01-01
82
+ BMS0081,Switche - Type A - Model 915,Switches,Each,88.87,51.91,138,472,SUP007,Y,2024-01-01
83
+ BMS0082,Seal - Type A - Model 618,Seals,Each,43.08,7.64,157,450,SUP002,Y,2024-01-01
84
+ BMS0083,Sensor - Type C - Model 750,Sensors,Each,183.19,78.95,89,422,SUP003,Y,2024-01-01
85
+ BMS0084,Pump - Type C - Model 730,Pumps,Each,427.12,221.61,173,438,SUP007,Y,2024-01-01
86
+ BMS0085,Switche - Type C - Model 430,Switches,Each,79.62,54.37,72,342,SUP008,Y,2024-01-01
87
+ BMS0086,Gasket - Type D - Model 683,Gaskets,Each,30.58,14.92,57,453,SUP006,Y,2024-01-01
88
+ BMS0087,Hose - Type D - Model 317,Hoses,Each,45.32,25.19,121,341,SUP009,Y,2024-01-01
89
+ BMS0088,Filter - Type B - Model 187,Filters,Each,31.89,29.52,192,323,SUP008,Y,2024-01-01
90
+ BMS0089,Valve - Type D - Model 911,Valves,Each,37.5,51.89,153,324,SUP005,Y,2024-01-01
91
+ BMS0090,Switche - Type C - Model 584,Switches,Each,58.74,29.53,190,369,SUP006,Y,2024-01-01
92
+ BMS0091,Valve - Type C - Model 413,Valves,Each,71.45,33.67,99,361,SUP002,Y,2024-01-01
93
+ BMS0092,Pump - Type B - Model 296,Pumps,Each,202.23,206.61,200,468,SUP010,Y,2024-01-01
94
+ BMS0093,Bearing - Type A - Model 952,Bearings,Each,53.29,37.79,95,354,SUP001,Y,2024-01-01
95
+ BMS0094,Pump - Type B - Model 380,Pumps,Each,137.3,87.16,124,264,SUP008,Y,2024-01-01
96
+ BMS0095,Belt - Type A - Model 687,Belts,Each,52.01,48.03,137,294,SUP001,Y,2024-01-01
97
+ BMS0096,Bearing - Type D - Model 216,Bearings,Each,128.65,52.86,68,495,SUP001,Y,2024-01-01
98
+ BMS0097,Hose - Type B - Model 930,Hoses,Each,61.34,27.72,113,260,SUP009,Y,2024-01-01
99
+ BMS0098,Sensor - Type B - Model 894,Sensors,Each,126.0,77.91,163,352,SUP010,Y,2024-01-01
100
+ BMS0099,Sensor - Type C - Model 682,Sensors,Each,141.26,95.89,75,306,SUP004,Y,2024-01-01
101
+ BMS0100,Bearing - Type A - Model 260,Bearings,Each,58.78,66.02,90,201,SUP007,Y,2024-01-01
data/promotions.csv ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ promo_id,item_code,start_date,end_date,discount_pct,promo_price,description
2
+ PROMO0001,BMS0084,2025-03-04,2025-04-03,15,363.05,15% Off - Holiday Special
3
+ PROMO0002,BMS0080,2025-06-09,2025-07-05,25,107.23,25% Off - Clearance
4
+ PROMO0003,BMS0049,2025-09-18,2025-10-11,30,79.9,30% Off - Flash Sale
5
+ PROMO0004,BMS0099,2025-07-02,2025-08-01,20,113.01,20% Off - Seasonal Sale
6
+ PROMO0005,BMS0095,2025-08-28,2025-09-16,10,46.81,10% Off - Seasonal Sale
7
+ PROMO0006,BMS0059,2025-09-11,2025-09-28,30,46.19,30% Off - Flash Sale
8
+ PROMO0007,BMS0079,2025-08-25,2025-09-13,15,65.75,15% Off - Clearance
9
+ PROMO0008,BMS0081,2025-08-06,2025-08-15,10,79.98,10% Off - Holiday Special
10
+ PROMO0009,BMS0093,2025-02-07,2025-03-08,20,42.63,20% Off - Holiday Special
11
+ PROMO0010,BMS0018,2025-03-09,2025-04-01,30,40.44,30% Off - Flash Sale
12
+ PROMO0011,BMS0020,2025-04-18,2025-04-29,10,31.57,10% Off - Flash Sale
13
+ PROMO0012,BMS0010,2025-06-23,2025-07-04,10,136.5,10% Off - Seasonal Sale
14
+ PROMO0013,BMS0025,2025-09-21,2025-10-14,25,19.47,25% Off - Holiday Special
15
+ PROMO0014,BMS0013,2025-03-01,2025-03-13,25,58.45,25% Off - Clearance
16
+ PROMO0015,BMS0080,2025-04-25,2025-05-09,20,114.38,20% Off - Seasonal Sale
17
+ PROMO0016,BMS0031,2025-09-27,2025-10-20,30,65.2,30% Off - Seasonal Sale
18
+ PROMO0017,BMS0061,2025-06-13,2025-07-08,25,27.64,25% Off - Holiday Special
19
+ PROMO0018,BMS0028,2025-07-11,2025-07-21,30,14.84,30% Off - Seasonal Sale
20
+ PROMO0019,BMS0011,2025-02-16,2025-03-17,15,28.68,15% Off - Holiday Special
21
+ PROMO0020,BMS0025,2025-09-23,2025-10-20,20,20.77,20% Off - Seasonal Sale
22
+ PROMO0021,BMS0040,2025-08-12,2025-08-22,30,9.69,30% Off - Flash Sale
data/requisitions.csv ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ req_id,item_code,quantity,req_date,status,qty,date
2
+ REQ9613,BMS0022,200.0,2025-11-12,Pending,,
3
+ REQ8649,BMS0054,50.0,2025-11-21,Pending,,
4
+ REQ5311,BMS0056,50.0,2025-11-18,Approved,,
5
+ REQ2710,BMS0069,200.0,2025-11-17,Ordered,,
6
+ REQ9804,BMS0098,50.0,2025-11-19,Approved,,
7
+ REQ8741,BMS0030,150.0,2025-11-20,Approved,,
8
+ REQ8550,BMS0015,100.0,2025-11-03,Ordered,,
9
+ REQ8326,BMS0024,150.0,2025-11-21,Ordered,,
10
+ REQ8459,BMS0077,50.0,2025-11-26,Ordered,,
11
+ REQ9846,BMS0004,150.0,2025-11-27,Pending,,
12
+ REQ3445,BMS0015,,,Pending,50.0,2025-11-29
data/suppliers.csv ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ id,name,lead_time,email
2
+ SUP001,Acme Filters Inc.,14,[email protected]
3
+ SUP002,Global Parts Co.,10,[email protected]
4
+ SUP003,Premium Components Ltd.,21,[email protected]
5
+ SUP004,Industrial Supply Corp.,7,[email protected]
6
+ SUP005,Quality Parts Group,12,[email protected]
7
+ SUP006,Precision Manufacturing,18,[email protected]
8
+ SUP007,Reliable Components,9,[email protected]
9
+ SUP008,Advanced Parts Solutions,15,[email protected]
10
+ SUP009,Express Supply Chain,5,[email protected]
11
+ SUP010,International Parts Hub,20,[email protected]
data/warehouses.csv ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ code,name,region
2
+ NA-01,North America - Columbus,NA
3
+ NA-02,North America - Memphis,NA
4
+ EU-01,Europe - Daventry,EU
5
+ EU-02,Europe - Rotterdam,EU
6
+ APAC-01,Asia Pacific - Singapore,APAC
7
+ APAC-02,Asia Pacific - Shanghai,APAC
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn[standard]==0.24.0
3
+ pandas==2.1.3
4
+ numpy==1.26.2
5
+ scikit-learn==1.3.2
6
+ statsmodels==0.14.0
7
+ ctransformers==0.2.27
8
+ fpdf==1.7.2
9
+ pydantic==2.5.0
10
+ python-multipart==0.0.6
static/index.html ADDED
@@ -0,0 +1,426 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>BMS AI Assistant</title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ :root {
11
+ --primary-color: #D01010;
12
+ /* Cummins Red */
13
+ --primary-dark: #A00C0C;
14
+ --bg-color: #F3F4F6;
15
+ --chat-bg: #FFFFFF;
16
+ --user-msg-bg: #D01010;
17
+ --user-msg-text: #FFFFFF;
18
+ --bot-msg-bg: #F3F4F6;
19
+ --bot-msg-text: #1F2937;
20
+ --border-color: #E5E7EB;
21
+ --text-color: #1F2937;
22
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
23
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
24
+ }
25
+
26
+ body {
27
+ font-family: 'Inter', sans-serif;
28
+ background-color: var(--bg-color);
29
+ margin: 0;
30
+ display: flex;
31
+ justify-content: center;
32
+ height: 100vh;
33
+ color: var(--text-color);
34
+ }
35
+
36
+ .chat-container {
37
+ width: 100%;
38
+ max-width: 900px;
39
+ background-color: var(--chat-bg);
40
+ box-shadow: var(--shadow-md);
41
+ overflow-y: auto;
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: 16px;
45
+ scroll-behavior: smooth;
46
+ }
47
+
48
+ .header {
49
+ display: none;
50
+ }
51
+
52
+ .message {
53
+ max-width: 80%;
54
+ padding: 14px 18px;
55
+ border-radius: 16px;
56
+ line-height: 1.5;
57
+ position: relative;
58
+ animation: fadeIn 0.3s ease-out;
59
+ display: flex;
60
+ align-items: flex-start;
61
+ gap: 12px;
62
+ font-size: 0.95rem;
63
+ box-shadow: var(--shadow-sm);
64
+ }
65
+
66
+ @keyframes fadeIn {
67
+ from {
68
+ opacity: 0;
69
+ transform: translateY(10px);
70
+ }
71
+
72
+ to {
73
+ opacity: 1;
74
+ transform: translateY(0);
75
+ }
76
+ }
77
+
78
+ .message.user {
79
+ align-self: flex-end;
80
+ background-color: var(--user-msg-bg);
81
+ color: var(--user-msg-text);
82
+ border-bottom-right-radius: 4px;
83
+ flex-direction: row-reverse;
84
+ }
85
+
86
+ .message.bot {
87
+ align-self: flex-start;
88
+ background-color: var(--bot-msg-bg);
89
+ color: var(--bot-msg-text);
90
+ border-bottom-left-radius: 4px;
91
+ border: 1px solid var(--border-color);
92
+ }
93
+
94
+ .icon-container {
95
+ width: 36px;
96
+ height: 36px;
97
+ border-radius: 50%;
98
+ display: flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ flex-shrink: 0;
102
+ background-color: white;
103
+ box-shadow: var(--shadow-sm);
104
+ }
105
+
106
+ .message.user .icon-container {
107
+ color: var(--primary-color);
108
+ }
109
+
110
+ .message.bot .icon-container {
111
+ background-color: var(--primary-color);
112
+ color: white;
113
+ }
114
+
115
+ .icon-container svg {
116
+ width: 20px;
117
+ height: 20px;
118
+ fill: currentColor;
119
+ }
120
+
121
+ .message-content {
122
+ flex: 1;
123
+ word-wrap: break-word;
124
+ }
125
+
126
+ .input-area {
127
+ padding: 20px;
128
+ border-top: 1px solid var(--border-color);
129
+ display: flex;
130
+ gap: 12px;
131
+ background-color: #fff;
132
+ align-items: center;
133
+ }
134
+
135
+ input[type="text"] {
136
+ flex: 1;
137
+ padding: 14px 20px;
138
+ border: 1px solid var(--border-color);
139
+ border-radius: 30px;
140
+ outline: none;
141
+ font-size: 1rem;
142
+ transition: all 0.2s;
143
+ font-family: inherit;
144
+ background-color: #F9FAFB;
145
+ }
146
+
147
+ input[type="text"]:focus {
148
+ border-color: var(--primary-color);
149
+ background-color: #fff;
150
+ box-shadow: 0 0 0 3px rgba(208, 16, 16, 0.1);
151
+ }
152
+
153
+ button {
154
+ background-color: var(--primary-color);
155
+ color: white;
156
+ border: none;
157
+ padding: 14px 28px;
158
+ border-radius: 30px;
159
+ cursor: pointer;
160
+ font-weight: 600;
161
+ transition: background-color 0.2s, transform 0.1s;
162
+ font-family: inherit;
163
+ display: flex;
164
+ align-items: center;
165
+ gap: 8px;
166
+ }
167
+
168
+ button:hover {
169
+ background-color: var(--primary-dark);
170
+ }
171
+
172
+ button:active {
173
+ transform: scale(0.98);
174
+ }
175
+
176
+ /* Table Styling */
177
+ .chart-container {
178
+ margin-top: 12px;
179
+ background: #fff;
180
+ border-radius: 8px;
181
+ border: 1px solid var(--border-color);
182
+ overflow: hidden;
183
+ }
184
+
185
+ table {
186
+ width: 100%;
187
+ border-collapse: collapse;
188
+ font-size: 0.9rem;
189
+ }
190
+
191
+ th,
192
+ td {
193
+ padding: 10px 14px;
194
+ text-align: left;
195
+ border-bottom: 1px solid var(--border-color);
196
+ }
197
+
198
+ th {
199
+ background-color: #F9FAFB;
200
+ font-weight: 600;
201
+ color: var(--text-color);
202
+ }
203
+
204
+ tr:last-child td {
205
+ border-bottom: none;
206
+ }
207
+
208
+ .download-btn {
209
+ display: inline-flex;
210
+ align-items: center;
211
+ gap: 8px;
212
+ background-color: var(--primary-color);
213
+ color: white;
214
+ padding: 10px 20px;
215
+ text-decoration: none;
216
+ border-radius: 25px;
217
+ font-weight: 600;
218
+ margin-top: 12px;
219
+ transition: background-color 0.2s;
220
+ font-size: 0.9rem;
221
+ }
222
+
223
+ .download-btn:hover {
224
+ background-color: var(--primary-dark);
225
+ }
226
+
227
+ /* Modal Styling */
228
+ .modal-overlay {
229
+ position: fixed;
230
+ top: 0;
231
+ left: 0;
232
+ width: 100%;
233
+ height: 100%;
234
+ background: rgba(0, 0, 0, 0.5);
235
+ display: flex;
236
+ justify-content: center;
237
+ align-items: center;
238
+ z-index: 1000;
239
+ }
240
+
241
+ .modal-content {
242
+ background: white;
243
+ padding: 2rem;
244
+ border-radius: 8px;
245
+ max-width: 500px;
246
+ width: 90%;
247
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
248
+ }
249
+
250
+ .modal-title {
251
+ font-size: 1.5rem;
252
+ font-weight: 600;
253
+ margin-bottom: 1rem;
254
+ color: var(--primary-color);
255
+ }
256
+
257
+ .modal-body {
258
+ margin-bottom: 1.5rem;
259
+ line-height: 1.6;
260
+ }
261
+
262
+ .modal-btn {
263
+ background: var(--primary-color);
264
+ color: white;
265
+ border: none;
266
+ padding: 0.75rem 1.5rem;
267
+ border-radius: 4px;
268
+ cursor: pointer;
269
+ font-weight: 600;
270
+ width: 100%;
271
+ }
272
+
273
+ .modal-btn:hover {
274
+ background-color: var(--primary-dark);
275
+ }
276
+ </style>
277
+ </head>
278
+
279
+ <body>
280
+ <div class="chat-container">
281
+ <div class="header">
282
+ <svg viewBox="0 0 24 24">
283
+ <path
284
+ d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a7 7 0 0 1 7 7h1a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v1a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1H2a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h1a7 7 0 0 1 7-7V5.73C9.4 5.39 9 4.74 9 4a2 2 0 0 1 2-2M7.5 13A2.5 2.5 0 0 0 5 15.5A2.5 2.5 0 0 0 7.5 18A2.5 2.5 0 0 0 10 15.5A2.5 2.5 0 0 0 7.5 13m9 0a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5a2.5 2.5 0 0 0-2.5-2.5M12 8a6 6 0 0 0-6 6h12a6 6 0 0 0-6-6z" />
285
+ </svg>
286
+ BMS AI Assistant
287
+ </div>
288
+
289
+ <div class="chat-window" id="chat-window">
290
+ <div class="message bot">
291
+ <div class="icon-container">
292
+ <svg viewBox="0 0 24 24">
293
+ <path
294
+ d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a7 7 0 0 1 7 7h1a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v1a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1H2a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h1a7 7 0 0 1 7-7V5.73C9.4 5.39 9 4.74 9 4a2 2 0 0 1 2-2M7.5 13A2.5 2.5 0 0 0 5 15.5A2.5 2.5 0 0 0 7.5 18A2.5 2.5 0 0 0 10 15.5A2.5 2.5 0 0 0 7.5 13m9 0a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5a2.5 2.5 0 0 0-2.5-2.5M12 8a6 6 0 0 0-6 6h12a6 6 0 0 0-6-6z" />
295
+ </svg>
296
+ </div>
297
+ <div class="message-content">
298
+ Hello! I am the BMS AI Assistant. How can I help you with demand forecasting, inventory, or supplier
299
+ information today?
300
+ </div>
301
+ </div>
302
+ </div>
303
+
304
+ <div class="input-area">
305
+ <input type="text" id="user-input" placeholder="Type your query..." autocomplete="off">
306
+ <button onclick="sendMessage()">
307
+ Send
308
+ <svg viewBox="0 0 24 24" style="width: 16px; height: 16px;">
309
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
310
+ </svg>
311
+ </button>
312
+ </div>
313
+ </div>
314
+
315
+ <!-- Disclaimer Modal -->
316
+ <div class="modal-overlay" id="disclaimer-modal">
317
+ <div class="modal-content">
318
+ <div class="modal-title">Welcome to BMS AI Assistant</div>
319
+ <div class="modal-body">
320
+ <p>I am an AI assistant designed to help you with: </p>
321
+ <ul>
322
+ <li>Demand Forecasting</li>
323
+ <li>Inventory Management</li>
324
+ <li>Supplier Information</li>
325
+ </ul>
326
+ <p><strong>Important Disclaimer:</strong></p>
327
+ <p>While I strive to provide accurate information, I am an AI and may occasionally make mistakes. Please
328
+ verify critical data with the respective BMS departments before making significant business
329
+ decisions.</p>
330
+ <p>By clicking "I Understand", you acknowledge these terms.</p>
331
+ </div>
332
+ <button class="modal-btn" onclick="closeModal()">I Understand</button>
333
+ </div>
334
+ </div>
335
+
336
+ <script>
337
+ const chatWindow = document.getElementById('chat-window');
338
+ const userInput = document.getElementById('user-input');
339
+ const modal = document.getElementById('disclaimer-modal');
340
+
341
+ function closeModal() {
342
+ modal.style.display = 'none';
343
+ }
344
+
345
+ userInput.addEventListener('keypress', function (e) {
346
+ if (e.key === 'Enter') {
347
+ sendMessage();
348
+ }
349
+ });
350
+
351
+ async function sendMessage() {
352
+ const text = userInput.value.trim();
353
+ if (!text) return;
354
+
355
+ addMessage(text, 'user');
356
+ userInput.value = '';
357
+
358
+ try {
359
+ const response = await fetch('/api/chat', {
360
+ method: 'POST',
361
+ headers: { 'Content-Type': 'application/json' },
362
+ body: JSON.stringify({ message: text })
363
+ });
364
+
365
+ const data = await response.json();
366
+ let botHtml = data.answer.replace(/\n/g, '<br>');
367
+
368
+ if (data.forecast && data.forecast.length > 0) {
369
+ botHtml += renderForecastTable(data.forecast);
370
+ }
371
+
372
+ addMessage(botHtml, 'bot', true);
373
+ } catch (error) {
374
+ console.error('Error:', error);
375
+ addMessage("Sorry, something went wrong connecting to the server.", 'bot');
376
+ }
377
+ }
378
+
379
+ function addMessage(text, sender, isHtml = false) {
380
+ const div = document.createElement('div');
381
+ div.className = `message ${sender}`;
382
+
383
+ const iconDiv = document.createElement('div');
384
+ iconDiv.className = 'icon-container';
385
+
386
+ if (sender === 'user') {
387
+ iconDiv.innerHTML = `<svg viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" /></svg>`;
388
+ } else {
389
+ iconDiv.innerHTML = `<svg viewBox="0 0 24 24"><path d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a7 7 0 0 1 7 7h1a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v1a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1H2a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h1a7 7 0 0 1 7-7V5.73C9.4 5.39 9 4.74 9 4a2 2 0 0 1 2-2M7.5 13A2.5 2.5 0 0 0 5 15.5A2.5 2.5 0 0 0 7.5 18A2.5 2.5 0 0 0 10 15.5A2.5 2.5 0 0 0 7.5 13m9 0a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5a2.5 2.5 0 0 0-2.5-2.5M12 8a6 6 0 0 0-6 6h12a6 6 0 0 0-6-6z" /></svg>`;
390
+ }
391
+
392
+ const contentDiv = document.createElement('div');
393
+ contentDiv.className = 'message-content';
394
+
395
+ if (isHtml) {
396
+ contentDiv.innerHTML = text;
397
+ } else {
398
+ contentDiv.textContent = text;
399
+ }
400
+
401
+ div.appendChild(iconDiv);
402
+ div.appendChild(contentDiv);
403
+
404
+ chatWindow.appendChild(div);
405
+ chatWindow.scrollTop = chatWindow.scrollHeight;
406
+ }
407
+
408
+ function renderForecastTable(forecastData) {
409
+ const displayData = forecastData.slice(0, 7);
410
+ let html = `<div class="chart-container"><table><thead><tr><th>Date</th><th>Qty</th></tr></thead><tbody>`;
411
+
412
+ displayData.forEach(row => {
413
+ html += `<tr><td>${row.date.split('T')[0]}</td><td>${row.qty}</td></tr>`;
414
+ });
415
+
416
+ if (forecastData.length > 7) {
417
+ html += `<tr><td colspan="2">...and ${forecastData.length - 7} more days</td></tr>`;
418
+ }
419
+
420
+ html += `</tbody></table></div>`;
421
+ return html;
422
+ }
423
+ </script>
424
+ </body>
425
+
426
+ </html>
static/reports/forecast_BMS0001_20251130230015.pdf ADDED
Binary file (2.43 kB). View file
 
static/reports/forecast_BMS0015_20251129164645.pdf ADDED
Binary file (2.43 kB). View file
 
static/reports/forecast_FS1234_20251126172817.pdf ADDED
Binary file (2.44 kB). View file
 
static/reports/forecast_FS1234_20251126173353.pdf ADDED
Binary file (2.44 kB). View file
 
static/reports/forecast_FS1234_20251126175819.pdf ADDED
Binary file (2.44 kB). View file
 
static/reports/forecast_FS1234_20251126180611.pdf ADDED
Binary file (2.45 kB). View file
 
static/reports/forecast_FS1234_20251126180710.pdf ADDED
Binary file (2.45 kB). View file
 
static/reports/forecast_FS1234_20251126181554.pdf ADDED
Binary file (2.44 kB). View file
 
static/reports/forecast_FS1234_20251126182220.pdf ADDED
Binary file (2.44 kB). View file
 
static/reports/forecast_FS1234_20251126183143.pdf ADDED
Binary file (2.44 kB). View file
 
static/reports/forecast_FS1234_20251126184230.pdf ADDED
Binary file (2.44 kB). View file
 
static/reports/forecast_TEST-ITEM_20251126172116.pdf ADDED
Binary file (1.5 kB). View file
 
static/reports/report_20251126172116.pdf ADDED
Binary file (1.34 kB). View file
 
static/reports/report_20251129165821.pdf ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
0
- �,6��a�_RK��&5sy��L��"��p��§u�8gQ���C��e���2c�
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ %PDF-1.3
2
+ 3 0 obj
3
+ <</Type /Page
4
+ /Parent 1 0 R
5
+ /Resources 2 0 R
6
+ /Contents 4 0 R>>
7
+ endobj
8
+ 4 0 obj
9
+ <</Filter /FlateDecode /Length 549>>
10
+ stream
11
+ x�e��n�0E����k����m�)
12
+ �ht�
13
+ U��/n�4J�
14
+ endstream
15
+ endobj
16
+ 1 0 obj
17
+ <</Type /Pages
18
+ /Kids [3 0 R ]
19
+ /Count 1
20
+ /MediaBox [0 0 595.28 841.89]
21
+ >>
22
+ endobj
23
+ 5 0 obj
24
+ <</Type /Font
25
+ /BaseFont /Helvetica-Bold
26
+ /Subtype /Type1
27
+ /Encoding /WinAnsiEncoding
28
+ >>
29
+ endobj
30
+ 6 0 obj
31
+ <</Type /Font
32
+ /BaseFont /Helvetica
33
+ /Subtype /Type1
34
+ /Encoding /WinAnsiEncoding
35
+ >>
36
+ endobj
37
+ 7 0 obj
38
+ <</Type /Font
39
+ /BaseFont /Helvetica-Oblique
40
+ /Subtype /Type1
41
+ /Encoding /WinAnsiEncoding
42
+ >>
43
+ endobj
44
+ 2 0 obj
45
+ <<
46
+ /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
47
+ /Font <<
48
+ /F1 5 0 R
49
+ /F2 6 0 R
50
+ /F3 7 0 R
51
+ >>
52
+ /XObject <<
53
+ >>
54
+ >>
55
+ endobj
56
+ 8 0 obj
57
+ <<
58
+ /Producer (PyFPDF 1.7.2 http://pyfpdf.googlecode.com/)
59
+ /CreationDate (D:20251129165821)
60
+ >>
61
+ endobj
62
+ 9 0 obj
63
+ <<
64
+ /Type /Catalog
65
+ /Pages 1 0 R
66
+ /OpenAction [3 0 R /FitH null]
67
+ /PageLayout /OneColumn
68
+ >>
69
+ endobj
70
+ xref
71
+ 0 10
72
+ 0000000000 65535 f
73
+ 0000000706 00000 n
74
+ 0000001094 00000 n
75
+ 0000000009 00000 n
76
+ 0000000087 00000 n
77
+ 0000000793 00000 n
78
+ 0000000894 00000 n
79
+ 0000000990 00000 n
80
+ 0000001218 00000 n
81
+ 0000001327 00000 n
82
+ trailer
83
+ <<
84
+ /Size 10
85
+ /Root 9 0 R
86
+ /Info 8 0 R
87
+ >>
88
+ startxref
89
+ 1430
90
+ %%EOF