Abdo-Eid commited on
Commit
02e4173
·
1 Parent(s): 0715650

project scaffolding

Browse files
Files changed (7) hide show
  1. .gitattributes +1 -1
  2. .gitignore +2 -0
  3. Dockerfile +18 -0
  4. README.md +51 -6
  5. app.py +58 -0
  6. requirements.txt +2 -0
  7. static/index.html +287 -0
.gitattributes CHANGED
@@ -32,4 +32,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
32
  *.xz filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
32
  *.xz filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ docker-compose.yaml
2
+ __pycache__/
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12-slim
2
+
3
+ # Create non-root user (recommended for HF Spaces)
4
+ RUN useradd -m -u 1000 user
5
+ USER user
6
+ ENV PATH="/home/user/.local/bin:$PATH"
7
+
8
+ WORKDIR /app
9
+
10
+ # Install dependencies
11
+ COPY --chown=user ./requirements.txt requirements.txt
12
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
13
+
14
+ # Copy app code
15
+ COPY --chown=user . /app
16
+
17
+ # Expose FastAPI app on port 7860 (required by HF Spaces)
18
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,13 +1,58 @@
1
  ---
2
  title: AgriBot
3
- emoji: 💻
4
  colorFrom: green
5
- colorTo: pink
6
- sdk: gradio
7
- sdk_version: 6.0.1
8
- app_file: app.py
9
  pinned: false
10
  license: mit
11
  ---
 
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: AgriBot
3
+ emoji: 🌱
4
  colorFrom: green
5
+ colorTo: green
6
+ sdk: docker
7
+ app_port: 7860
 
8
  pinned: false
9
  license: mit
10
  ---
11
+ # 🌱 Agricash Chatbot Overlay
12
 
13
+ This project provides a fully customizable chatbot overlay that sits on top of the Agricash web application.
14
+ It includes:
15
+
16
+ ### ✔ Custom HTML/CSS/JS Chat Widget
17
+ The chat widget is styled with an agriculture theme and appears as a floating assistant on the page.
18
+
19
+ ### ✔ FastAPI Backend
20
+ The backend is implemented using FastAPI and runs inside a Hugging Face Docker Space.
21
+ Endpoint structure:
22
+
23
+ - `GET /` → Serves the chat UI (index.html)
24
+ - `POST /chat` → Handles chatbot messages (currently placeholder logic)
25
+
26
+ ### ✔ Iframe Integration
27
+ The chatbot overlay loads the Agricash website inside an iframe while keeping the chat widget on top.
28
+
29
+ ### ✔ Ready for LLM Integration
30
+ The backend is designed so the `/chat` endpoint can easily be updated to use any Hugging Face-supported LLM (e.g., Llama 3, Mixtral, Phi-3).
31
+
32
+ ---
33
+
34
+ ## 🚀 Local Development
35
+
36
+ Build the Docker image:
37
+
38
+ ```bash
39
+ docker build -t agricash-chatbot .
40
+ ````
41
+
42
+ Run it locally:
43
+
44
+ ```bash
45
+ docker run -p 7860:7860 agricash-chatbot
46
+ ```
47
+
48
+ Open in browser:
49
+
50
+ ```
51
+ http://localhost:7860/
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 📞 Contact
57
+
58
+ For improvements or future integration with real LLM models, feel free to reach out.
app.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.responses import HTMLResponse
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from pydantic import BaseModel
6
+ from pathlib import Path
7
+
8
+ app = FastAPI()
9
+
10
+ # CORS – allow embedding and external calls if needed
11
+ app.add_middleware(
12
+ CORSMiddleware,
13
+ allow_origins=["*"], # you can restrict this later
14
+ allow_methods=["*"],
15
+ allow_headers=["*"],
16
+ )
17
+
18
+ # Serve static files (we use it mainly for index.html now)
19
+ app.mount("/static", StaticFiles(directory="static"), name="static")
20
+
21
+
22
+ # ----------------- MODELS -----------------
23
+
24
+ class ChatRequest(BaseModel):
25
+ message: str
26
+
27
+
28
+ # ----------------- ROUTES -----------------
29
+
30
+ @app.get("/", response_class=HTMLResponse)
31
+ def serve_html():
32
+ """
33
+ Serve the chat overlay HTML as the root page.
34
+ """
35
+ index_path = Path("static/index.html")
36
+ return index_path.read_text(encoding="utf-8")
37
+
38
+
39
+ @app.post("/chat")
40
+ def chat_endpoint(req: ChatRequest):
41
+ """
42
+ Chat endpoint.
43
+ For now it returns a placeholder response.
44
+ Later you will plug in your LLM here.
45
+ """
46
+ user_message = req.message.strip()
47
+
48
+ # Placeholder logic – replace with real model call later
49
+ if not user_message:
50
+ reply = "Please type a message to start the conversation."
51
+ else:
52
+ reply = (
53
+ "This is a placeholder response. "
54
+ "The AI model will be integrated here later.\n\n"
55
+ f"You said: \"{user_message}\""
56
+ )
57
+
58
+ return {"response": reply}
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
static/index.html ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ar" dir="rtl">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>مساعد أجريكاش</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+
8
+ <style>
9
+ * {
10
+ box-sizing: border-box;
11
+ margin: 0;
12
+ padding: 0;
13
+ font-family: "Tahoma", "Cairo", sans-serif;
14
+ }
15
+
16
+ html, body {
17
+ width: 100%;
18
+ height: 100%;
19
+ overflow: hidden;
20
+ background: #f4f7f2;
21
+ }
22
+
23
+ /* موقع أجريكاش */
24
+ .background-app {
25
+ position: fixed;
26
+ inset: 0;
27
+ width: 100%;
28
+ height: 100%;
29
+ border: none;
30
+ z-index: 1;
31
+ }
32
+
33
+ /* زر فتح الشات */
34
+ .chat-toggle {
35
+ position: fixed;
36
+ bottom: 30px;
37
+ right: 30px;
38
+ width: 65px;
39
+ height: 65px;
40
+ border-radius: 50%;
41
+ border: none;
42
+ cursor: pointer;
43
+ background: linear-gradient(135deg, #3b7f3b, #6abf4b);
44
+ color: #fff;
45
+ font-size: 32px;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
50
+ z-index: 3;
51
+ }
52
+
53
+ /* صندوق الشات */
54
+ .chat-container {
55
+ position: fixed;
56
+ bottom: 120px;
57
+ right: 30px;
58
+ width: 420px;
59
+ max-height: 75vh;
60
+ background: #ffffff;
61
+ border-radius: 20px;
62
+ box-shadow: 0 18px 40px rgba(0, 0, 0, 0.25);
63
+ display: flex;
64
+ flex-direction: column;
65
+ overflow: hidden;
66
+ z-index: 3;
67
+ border: 1px solid #d8e6d2;
68
+ display: none; /* سيتم فتحه تلقائياً من الجافاسكربت */
69
+ }
70
+
71
+ /* هيدر الشات */
72
+ .chat-header {
73
+ background: linear-gradient(135deg, #2f6f2f, #4fa953);
74
+ color: #fff;
75
+ padding: 14px 16px;
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 12px;
79
+ font-size: 18px;
80
+ }
81
+
82
+ .chat-header-icon {
83
+ font-size: 26px;
84
+ }
85
+
86
+ /* الرسائل */
87
+ .chat-messages {
88
+ padding: 14px;
89
+ background: #f4f9f2;
90
+ flex: 1;
91
+ overflow-y: auto;
92
+ font-size: 16px;
93
+ }
94
+
95
+ .message-row {
96
+ margin-bottom: 12px;
97
+ display: flex;
98
+ }
99
+
100
+ .message-row.user {
101
+ justify-content: flex-end;
102
+ }
103
+
104
+ .message-row.bot {
105
+ justify-content: flex-start;
106
+ }
107
+
108
+ .message-bubble {
109
+ max-width: 80%;
110
+ padding: 10px 14px;
111
+ border-radius: 14px;
112
+ white-space: pre-wrap;
113
+ line-height: 1.5;
114
+ }
115
+
116
+ .message-row.user .message-bubble {
117
+ background: #e6f5da;
118
+ border: 1px solid #cde6bc;
119
+ border-bottom-right-radius: 4px;
120
+ }
121
+
122
+ .message-row.bot .message-bubble {
123
+ background: #ffffff;
124
+ border: 1px solid #d9e1d3;
125
+ border-bottom-left-radius: 4px;
126
+ }
127
+
128
+ /* حقل الإدخال */
129
+ .chat-input-area {
130
+ border-top: 1px solid #dfe8d8;
131
+ background: #fdfefb;
132
+ padding: 10px;
133
+ display: flex;
134
+ gap: 8px;
135
+ align-items: center;
136
+ }
137
+
138
+ .chat-input {
139
+ flex: 1;
140
+ border-radius: 999px;
141
+ border: 1px solid #cddac4;
142
+ padding: 10px 16px;
143
+ font-size: 16px;
144
+ outline: none;
145
+ }
146
+
147
+ .chat-send-btn {
148
+ border-radius: 999px;
149
+ border: none;
150
+ background: #6abf4b;
151
+ color: #ffffff;
152
+ padding: 10px 14px;
153
+ font-size: 18px;
154
+ cursor: pointer;
155
+ }
156
+
157
+ @media (max-width: 480px) {
158
+ .chat-container {
159
+ right: 10px;
160
+ left: 10px;
161
+ width: auto;
162
+ max-height: 75vh;
163
+ }
164
+
165
+ .chat-toggle {
166
+ right: 16px;
167
+ bottom: 16px;
168
+ }
169
+ }
170
+ </style>
171
+ </head>
172
+
173
+ <body>
174
+
175
+ <!-- موقع أجريكاش -->
176
+ <iframe
177
+ src="https://agricash.app/"
178
+ class="background-app"
179
+ title="Agricash App"
180
+ ></iframe>
181
+
182
+ <!-- زر الشات -->
183
+ <button class="chat-toggle" id="chatToggle">💬</button>
184
+
185
+ <!-- صندوق الشات -->
186
+ <div class="chat-container" id="chatContainer">
187
+ <div class="chat-header">
188
+ <div class="chat-header-icon">🌱</div>
189
+ <div>مساعد أجريكاش الذكي</div>
190
+ </div>
191
+
192
+ <div class="chat-messages" id="chatMessages"></div>
193
+
194
+ <div class="chat-input-area">
195
+ <input
196
+ id="chatInput"
197
+ type="text"
198
+ class="chat-input"
199
+ placeholder="اكتب رسالتك هنا..."
200
+ />
201
+ <button class="chat-send-btn" id="chatSendBtn">➤</button>
202
+ </div>
203
+ </div>
204
+
205
+ <script>
206
+ const chatToggle = document.getElementById("chatToggle");
207
+ const chatContainer = document.getElementById("chatContainer");
208
+ const chatMessages = document.getElementById("chatMessages");
209
+ const chatInput = document.getElementById("chatInput");
210
+ const chatSendBtn = document.getElementById("chatSendBtn");
211
+
212
+ let isOpen = false;
213
+ let isSending = false;
214
+
215
+ function toggleChat(forceOpen = false) {
216
+ isOpen = forceOpen ? true : !isOpen;
217
+ chatContainer.style.display = isOpen ? "flex" : "none";
218
+ }
219
+
220
+ chatToggle.addEventListener("click", () => toggleChat());
221
+
222
+ function appendMessage(role, text) {
223
+ const row = document.createElement("div");
224
+ row.className = "message-row " + role;
225
+
226
+ const bubble = document.createElement("div");
227
+ bubble.className = "message-bubble";
228
+ bubble.textContent = text;
229
+
230
+ row.appendChild(bubble);
231
+ chatMessages.appendChild(row);
232
+ chatMessages.scrollTop = chatMessages.scrollHeight;
233
+ }
234
+
235
+ async function sendMessage() {
236
+ const text = chatInput.value.trim();
237
+ if (!text || isSending) return;
238
+
239
+ appendMessage("user", text);
240
+ chatInput.value = "";
241
+ chatInput.focus();
242
+
243
+ isSending = true;
244
+ chatSendBtn.disabled = true;
245
+
246
+ // thinking placeholder
247
+ const thinkingRow = document.createElement("div");
248
+ thinkingRow.className = "message-row bot";
249
+ const thinkingBubble = document.createElement("div");
250
+ thinkingBubble.className = "message-bubble";
251
+ thinkingBubble.textContent = "🤖 جاري التفكير...";
252
+ thinkingRow.appendChild(thinkingBubble);
253
+ chatMessages.appendChild(thinkingRow);
254
+
255
+ try {
256
+ const res = await fetch("/chat", {
257
+ method: "POST",
258
+ headers: { "Content-Type": "application/json" },
259
+ body: JSON.stringify({ message: text }),
260
+ });
261
+
262
+ const data = await res.json();
263
+ thinkingBubble.textContent = data.response || "لا توجد إجابة.";
264
+ } catch (err) {
265
+ thinkingBubble.textContent = "خطأ في الاتصال بالخادم.";
266
+ }
267
+
268
+ isSending = false;
269
+ chatSendBtn.disabled = false;
270
+ }
271
+
272
+ chatSendBtn.addEventListener("click", sendMessage);
273
+
274
+ chatInput.addEventListener("keydown", (e) => {
275
+ if (e.key === "Enter") {
276
+ e.preventDefault();
277
+ sendMessage();
278
+ }
279
+ });
280
+
281
+ // ⚡ OPEN CHAT AUTOMATICALLY ON PAGE LOAD
282
+ window.onload = () => {
283
+ toggleChat(true);
284
+ };
285
+ </script>
286
+ </body>
287
+ </html>