Skip to content

Commit 0c03a19

Browse files
authored
Merge pull request #5 from MicrosoftCloudEssentials-LearningHub/single-agent
single-agent architecture
2 parents ea03646 + 0d8a28a commit 0c03a19

15 files changed

Lines changed: 831 additions & 19 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
*.terraform.lock.hcl
44
.terraform.lock.hcl
55
*src/.env
6+
app-logs.zip
7+
LogFiles
8+
deploy.log
69

710
# .tfstate files
811
*.tfstate

TROUBLESHOOTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ terraform apply
347347

348348
<!-- START BADGE -->
349349
<div align="center">
350-
<img src="https://img.shields.io/badge/Total%20views-1410-limegreen" alt="Total views">
351-
<p>Refresh Date: 2025-11-24</p>
350+
<img src="https://img.shields.io/badge/Total%20views-1543-limegreen" alt="Total views">
351+
<p>Refresh Date: 2025-11-25</p>
352352
</div>
353353
<!-- END BADGE -->

src/DATA_PIPELINE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ az search index show-statistics \
277277

278278
<!-- START BADGE -->
279279
<div align="center">
280-
<img src="https://img.shields.io/badge/Total%20views-1410-limegreen" alt="Total views">
281-
<p>Refresh Date: 2025-11-24</p>
280+
<img src="https://img.shields.io/badge/Total%20views-1543-limegreen" alt="Total views">
281+
<p>Refresh Date: 2025-11-25</p>
282282
</div>
283283
<!-- END BADGE -->

src/Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM python:3.11-slim
2+
3+
WORKDIR /app
4+
5+
# Copy requirements first for better caching
6+
COPY requirements.txt .
7+
RUN pip install --no-cache-dir -r requirements.txt
8+
9+
# Copy application code
10+
COPY . .
11+
12+
# Expose port
13+
EXPOSE 8000
14+
15+
# Run the application
16+
CMD ["uvicorn", "chat_app:app", "--host", "0.0.0.0", "--port", "8000"]

src/app/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Zava AI Shopping Assistant Application

src/app/static/.gitkeep

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Placeholder file to preserve the static directory in git
2+
# Add static assets like CSS, JavaScript, or images here as needed

src/app/templates/index.html

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Zava AI Shopping Assistant</title>
7+
<style>
8+
* {
9+
margin: 0;
10+
padding: 0;
11+
box-sizing: border-box;
12+
}
13+
14+
body {
15+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
16+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17+
height: 100vh;
18+
display: flex;
19+
justify-content: center;
20+
align-items: center;
21+
}
22+
23+
.chat-container {
24+
width: 90%;
25+
max-width: 800px;
26+
height: 90vh;
27+
background: white;
28+
border-radius: 20px;
29+
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
30+
display: flex;
31+
flex-direction: column;
32+
overflow: hidden;
33+
}
34+
35+
.chat-header {
36+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
37+
color: white;
38+
padding: 20px;
39+
text-align: center;
40+
}
41+
42+
.chat-header h1 {
43+
font-size: 24px;
44+
margin-bottom: 5px;
45+
}
46+
47+
.chat-header p {
48+
font-size: 14px;
49+
opacity: 0.9;
50+
}
51+
52+
.chat-messages {
53+
flex: 1;
54+
overflow-y: auto;
55+
padding: 20px;
56+
background: #f7f7f7;
57+
}
58+
59+
.message {
60+
margin-bottom: 15px;
61+
display: flex;
62+
animation: fadeIn 0.3s ease-in;
63+
}
64+
65+
@keyframes fadeIn {
66+
from { opacity: 0; transform: translateY(10px); }
67+
to { opacity: 1; transform: translateY(0); }
68+
}
69+
70+
.message.user {
71+
justify-content: flex-end;
72+
}
73+
74+
.message-content {
75+
max-width: 70%;
76+
padding: 12px 16px;
77+
border-radius: 18px;
78+
word-wrap: break-word;
79+
}
80+
81+
.message.user .message-content {
82+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
83+
color: white;
84+
}
85+
86+
.message.assistant .message-content {
87+
background: white;
88+
color: #333;
89+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
90+
}
91+
92+
.message.system .message-content {
93+
background: #fff3cd;
94+
color: #856404;
95+
max-width: 100%;
96+
text-align: center;
97+
}
98+
99+
.chat-input-container {
100+
padding: 20px;
101+
background: white;
102+
border-top: 1px solid #e0e0e0;
103+
display: flex;
104+
gap: 10px;
105+
}
106+
107+
#messageInput {
108+
flex: 1;
109+
padding: 12px 16px;
110+
border: 2px solid #e0e0e0;
111+
border-radius: 25px;
112+
font-size: 14px;
113+
outline: none;
114+
transition: border-color 0.3s;
115+
}
116+
117+
#messageInput:focus {
118+
border-color: #667eea;
119+
}
120+
121+
#sendButton {
122+
padding: 12px 30px;
123+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
124+
color: white;
125+
border: none;
126+
border-radius: 25px;
127+
font-size: 14px;
128+
font-weight: 600;
129+
cursor: pointer;
130+
transition: transform 0.2s;
131+
}
132+
133+
#sendButton:hover {
134+
transform: scale(1.05);
135+
}
136+
137+
#sendButton:active {
138+
transform: scale(0.95);
139+
}
140+
141+
#sendButton:disabled {
142+
opacity: 0.5;
143+
cursor: not-allowed;
144+
}
145+
146+
.typing-indicator {
147+
display: none;
148+
padding: 12px 16px;
149+
background: white;
150+
border-radius: 18px;
151+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
152+
width: fit-content;
153+
}
154+
155+
.typing-indicator.active {
156+
display: block;
157+
}
158+
159+
.typing-indicator span {
160+
height: 8px;
161+
width: 8px;
162+
background: #667eea;
163+
border-radius: 50%;
164+
display: inline-block;
165+
margin: 0 2px;
166+
animation: typing 1.4s infinite;
167+
}
168+
169+
.typing-indicator span:nth-child(2) {
170+
animation-delay: 0.2s;
171+
}
172+
173+
.typing-indicator span:nth-child(3) {
174+
animation-delay: 0.4s;
175+
}
176+
177+
@keyframes typing {
178+
0%, 60%, 100% { transform: translateY(0); }
179+
30% { transform: translateY(-10px); }
180+
}
181+
</style>
182+
</head>
183+
<body>
184+
<div class="chat-container">
185+
<div class="chat-header">
186+
<h1>🏠 Zava AI Shopping Assistant</h1>
187+
<p>Your DIY Project Helper</p>
188+
</div>
189+
190+
<div class="chat-messages" id="chatMessages">
191+
<div class="message system">
192+
<div class="message-content">
193+
Welcome to Zava! I'm here to help you with your DIY projects. Ask me about our products, get recommendations, or find store locations.
194+
</div>
195+
</div>
196+
</div>
197+
198+
<div class="chat-input-container">
199+
<input type="text" id="messageInput" placeholder="Ask about paints, tools, or DIY tips..." />
200+
<button id="sendButton">Send</button>
201+
</div>
202+
</div>
203+
204+
<script>
205+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
206+
const ws = new WebSocket(`${protocol}//${window.location.host}/ws`);
207+
const messagesContainer = document.getElementById('chatMessages');
208+
const messageInput = document.getElementById('messageInput');
209+
const sendButton = document.getElementById('sendButton');
210+
211+
let typingIndicator = null;
212+
213+
ws.onopen = () => {
214+
console.log('WebSocket connected');
215+
addSystemMessage('Connected to Zava Assistant');
216+
};
217+
218+
ws.onclose = () => {
219+
console.log('WebSocket disconnected');
220+
addSystemMessage('Disconnected from server');
221+
};
222+
223+
ws.onerror = (error) => {
224+
console.error('WebSocket error:', error);
225+
addSystemMessage('Connection error occurred');
226+
};
227+
228+
ws.onmessage = (event) => {
229+
const data = JSON.parse(event.data);
230+
removeTypingIndicator();
231+
232+
if (data.answer) {
233+
addMessage(data.answer, 'assistant');
234+
}
235+
236+
if (data.error) {
237+
addSystemMessage(`Error: ${data.error}`);
238+
}
239+
};
240+
241+
function addMessage(text, type) {
242+
const messageDiv = document.createElement('div');
243+
messageDiv.className = `message ${type}`;
244+
245+
const contentDiv = document.createElement('div');
246+
contentDiv.className = 'message-content';
247+
contentDiv.textContent = text;
248+
249+
messageDiv.appendChild(contentDiv);
250+
messagesContainer.appendChild(messageDiv);
251+
messagesContainer.scrollTop = messagesContainer.scrollHeight;
252+
}
253+
254+
function addSystemMessage(text) {
255+
const messageDiv = document.createElement('div');
256+
messageDiv.className = 'message system';
257+
258+
const contentDiv = document.createElement('div');
259+
contentDiv.className = 'message-content';
260+
contentDiv.textContent = text;
261+
262+
messageDiv.appendChild(contentDiv);
263+
messagesContainer.appendChild(messageDiv);
264+
messagesContainer.scrollTop = messagesContainer.scrollHeight;
265+
}
266+
267+
function showTypingIndicator() {
268+
typingIndicator = document.createElement('div');
269+
typingIndicator.className = 'message assistant';
270+
271+
const indicator = document.createElement('div');
272+
indicator.className = 'typing-indicator active';
273+
indicator.innerHTML = '<span></span><span></span><span></span>';
274+
275+
typingIndicator.appendChild(indicator);
276+
messagesContainer.appendChild(typingIndicator);
277+
messagesContainer.scrollTop = messagesContainer.scrollHeight;
278+
}
279+
280+
function removeTypingIndicator() {
281+
if (typingIndicator) {
282+
typingIndicator.remove();
283+
typingIndicator = null;
284+
}
285+
}
286+
287+
function sendMessage() {
288+
const message = messageInput.value.trim();
289+
if (!message || ws.readyState !== WebSocket.OPEN) return;
290+
291+
addMessage(message, 'user');
292+
ws.send(message);
293+
messageInput.value = '';
294+
showTypingIndicator();
295+
}
296+
297+
sendButton.addEventListener('click', sendMessage);
298+
299+
messageInput.addEventListener('keypress', (e) => {
300+
if (e.key === 'Enter') {
301+
sendMessage();
302+
}
303+
});
304+
</script>
305+
</body>
306+
</html>

src/app/tools/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Agent tools module

0 commit comments

Comments
 (0)