Charles Azam commited on
Commit
22030f5
·
1 Parent(s): 9aa04df

feat: improve gradio layout

Browse files
Files changed (1) hide show
  1. gradio_app.py +361 -12
gradio_app.py CHANGED
@@ -1,24 +1,373 @@
1
  import gradio as gr
 
2
  from deepengineer.backend.gradio_tools import run_agent_stream
3
  from deepengineer.common_path import DATA_DIR
4
 
5
- with gr.Blocks() as demo:
6
- gr.Markdown("# Agent Interface with Real‑Time Tool Logging")
7
- user_input = gr.Textbox(label="User Message")
 
 
8
 
9
- log_output = gr.Textbox(label="Tool Invocation Log", interactive=False)
 
 
 
 
 
 
 
 
10
 
11
- agent_output = gr.Markdown(
12
- label="Agent Response",
13
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- send = gr.Button("Send")
16
- send.click(
17
- fn=run_agent_stream,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  inputs=[user_input],
19
  outputs=[agent_output, log_output],
20
- concurrency_limit=2,
 
 
 
 
21
  )
22
 
23
  if __name__ == "__main__":
24
- demo.launch(allowed_paths=[DATA_DIR])
 
 
 
 
 
 
1
  import gradio as gr
2
+ import time
3
  from deepengineer.backend.gradio_tools import run_agent_stream
4
  from deepengineer.common_path import DATA_DIR
5
 
6
+ # Custom CSS for hardware engineering theme
7
+ custom_css = """
8
+ .gradio-container {
9
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10
+ }
11
 
12
+ .main-header {
13
+ background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
14
+ color: white;
15
+ padding: 2rem;
16
+ border-radius: 10px;
17
+ margin-bottom: 2rem;
18
+ text-align: center;
19
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
20
+ }
21
 
22
+ .main-header h1 {
23
+ margin: 0;
24
+ font-size: 2.5rem;
25
+ font-weight: 700;
26
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
27
+ }
28
+
29
+ .main-header p {
30
+ margin: 0.5rem 0 0 0;
31
+ font-size: 1.1rem;
32
+ opacity: 0.9;
33
+ }
34
+
35
+ .input-container {
36
+ background: transparent;
37
+ padding: 1rem;
38
+ border-radius: 10px;
39
+ margin-bottom: 1rem;
40
+ }
41
+
42
+ .log-container {
43
+ background: transparent;
44
+ border-radius: 10px;
45
+ padding: 1rem;
46
+ margin-bottom: 2rem;
47
+ }
48
+
49
+ .log-header {
50
+ display: flex;
51
+ align-items: center;
52
+ margin-bottom: 1rem;
53
+ padding: 0.5rem;
54
+ background: var(--background-fill-secondary);
55
+ border-radius: 5px;
56
+ }
57
+
58
+ .log-icon {
59
+ margin-right: 0.5rem;
60
+ color: var(--body-text-color);
61
+ }
62
+
63
+ .log-title {
64
+ font-weight: 600;
65
+ color: var(--body-text-color);
66
+ margin: 0;
67
+ }
68
+
69
+ .processing-indicator {
70
+ display: flex;
71
+ align-items: center;
72
+ justify-content: center;
73
+ padding: 1rem;
74
+ background: linear-gradient(135deg, #28a745, #20c997);
75
+ color: white;
76
+ border-radius: 10px;
77
+ margin-bottom: 1rem;
78
+ animation: pulse 2s infinite;
79
+ }
80
+
81
+ @keyframes pulse {
82
+ 0% { opacity: 1; }
83
+ 50% { opacity: 0.7; }
84
+ 100% { opacity: 1; }
85
+ }
86
+
87
+ .spinner {
88
+ border: 3px solid #f3f3f3;
89
+ border-top: 3px solid #3498db;
90
+ border-radius: 50%;
91
+ width: 20px;
92
+ height: 20px;
93
+ animation: spin 1s linear infinite;
94
+ margin-right: 0.5rem;
95
+ }
96
+
97
+ @keyframes spin {
98
+ 0% { transform: rotate(0deg); }
99
+ 100% { transform: rotate(360deg); }
100
+ }
101
+
102
+ .response-container {
103
+ background: var(--background-fill-primary);
104
+ border: 1px solid var(--border-color-primary);
105
+ border-radius: 10px;
106
+ padding: 1.5rem;
107
+ margin-top: 1rem;
108
+ }
109
+
110
+ .send-button {
111
+ background: linear-gradient(135deg, #007bff, #0056b3) !important;
112
+ color: white !important;
113
+ border: none !important;
114
+ padding: 0.5rem 1.5rem !important;
115
+ border-radius: 8px !important;
116
+ font-weight: 600 !important;
117
+ font-size: 0.95rem !important;
118
+ transition: all 0.3s ease !important;
119
+ box-shadow: 0 2px 8px rgba(0,123,255,0.3) !important;
120
+ min-width: 120px !important;
121
+ max-width: 200px !important;
122
+ }
123
+
124
+ .send-button:hover {
125
+ transform: translateY(-1px) !important;
126
+ box-shadow: 0 4px 12px rgba(0,123,255,0.4) !important;
127
+ }
128
+
129
+ .send-button:disabled {
130
+ background: #6c757d !important;
131
+ transform: none !important;
132
+ box-shadow: none !important;
133
+ }
134
+
135
+ .log-text {
136
+ font-family: 'Courier New', monospace;
137
+ font-size: 0.9rem;
138
+ line-height: 1.4;
139
+ color: var(--body-text-color);
140
+ background: var(--background-fill-secondary);
141
+ border: 1px solid var(--border-color-primary);
142
+ border-radius: 5px;
143
+ padding: 1rem;
144
+ max-height: 300px;
145
+ overflow-y: auto;
146
+ }
147
+
148
+ .log-entry {
149
+ margin-bottom: 0.5rem;
150
+ padding: 0.25rem 0;
151
+ border-bottom: 1px solid var(--border-color-primary);
152
+ }
153
+
154
+ .log-entry:last-child {
155
+ border-bottom: none;
156
+ }
157
 
158
+ .timestamp {
159
+ color: var(--body-text-color-subdued);
160
+ font-size: 0.8rem;
161
+ }
162
+
163
+ .tool-name {
164
+ color: #007bff;
165
+ font-weight: 600;
166
+ }
167
+
168
+ .tool-description {
169
+ color: var(--body-text-color);
170
+ }
171
+
172
+ .hardware-icon {
173
+ font-size: 2rem;
174
+ margin-right: 1rem;
175
+ }
176
+
177
+ /* Dark mode specific adjustments */
178
+ .dark .input-container {
179
+ background: transparent;
180
+ }
181
+
182
+ .dark .log-container {
183
+ background: transparent;
184
+ }
185
+
186
+ .dark .response-container {
187
+ background: var(--background-fill-primary);
188
+ border-color: var(--border-color-primary);
189
+ }
190
+
191
+ .dark .log-text {
192
+ background: var(--background-fill-secondary);
193
+ border-color: var(--border-color-primary);
194
+ color: var(--body-text-color);
195
+ }
196
+
197
+ .dark .log-header {
198
+ background: var(--background-fill-secondary);
199
+ }
200
+
201
+ .dark .log-title {
202
+ color: var(--body-text-color);
203
+ }
204
+
205
+ .dark .log-icon {
206
+ color: var(--body-text-color);
207
+ }
208
+
209
+ .dark .timestamp {
210
+ color: var(--body-text-color-subdued);
211
+ }
212
+
213
+ .dark .tool-description {
214
+ color: var(--body-text-color);
215
+ }
216
+ """
217
+
218
+ def format_log_entry(log_text):
219
+ """Format log entries with timestamps and better structure"""
220
+ if not log_text:
221
+ return ""
222
+
223
+ lines = log_text.split('\n')
224
+ formatted_lines = []
225
+
226
+ for line in lines:
227
+ if line.strip():
228
+ # Add timestamp and format tool calls
229
+ timestamp = time.strftime("%H:%M:%S")
230
+ if "Tool:" in line or "tool:" in line:
231
+ formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-name'>🔧 {line.strip()}</span></div>")
232
+ elif "Searching" in line or "searching" in line:
233
+ formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-name'>🔍 {line.strip()}</span></div>")
234
+ elif "Processing" in line or "processing" in line:
235
+ formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-name'>⚙️ {line.strip()}</span></div>")
236
+ elif "Drawing" in line or "drawing" in line:
237
+ formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-name'>🎨 {line.strip()}</span></div>")
238
+ else:
239
+ formatted_lines.append(f"<div class='log-entry'><span class='timestamp'>[{timestamp}]</span> <span class='tool-description'>{line.strip()}</span></div>")
240
+
241
+ return "".join(formatted_lines)
242
+
243
+ def run_agent_with_ui(user_input: str):
244
+ """Enhanced agent runner with better UI feedback"""
245
+ if not user_input.strip():
246
+ return "", ""
247
+
248
+ # Initialize empty outputs
249
+ log_buffer = ""
250
+ agent_output = ""
251
+
252
+ # Stream from the agent
253
+ for agent_result, log_result in run_agent_stream(user_input):
254
+ if log_result:
255
+ log_buffer = log_result
256
+ if agent_result:
257
+ agent_output = agent_result
258
+
259
+ # Format the log for display
260
+ formatted_log = format_log_entry(log_buffer)
261
+
262
+ yield agent_output, formatted_log
263
+
264
+ # Create the Gradio interface
265
+ with gr.Blocks(css=custom_css, title="DeepDraft - Hardware Engineering Assistant") as demo:
266
+
267
+ # Header with hardware engineering theme
268
+ with gr.Row():
269
+ gr.HTML("""
270
+ <div class="main-header">
271
+ <h1>🔧 DeepEngineer</h1>
272
+ <p>Advanced Hardware Engineering Assistant</p>
273
+ <p>Powered by AI-driven analysis and visualization</p>
274
+ </div>
275
+ """)
276
+
277
+ # Main input section
278
+ with gr.Row():
279
+ with gr.Column(scale=3):
280
+ gr.HTML('<div class="input-container">')
281
+ user_input = gr.Textbox(
282
+ label="🔍 Describe your hardware engineering query",
283
+ placeholder="e.g., 'Design a thermal management system for a high-power LED array' or 'Analyze the stress distribution in a mechanical bracket'",
284
+ lines=3,
285
+ max_lines=5
286
+ )
287
+ gr.HTML('</div>')
288
+
289
+ # Send button
290
+ with gr.Row():
291
+ send_button = gr.Button("🚀 Launch Analysis", variant="primary", elem_classes=["send-button"])
292
+
293
+ # Processing indicator (hidden by default)
294
+ processing_indicator = gr.HTML(
295
+ """
296
+ <div class="processing-indicator" style="display: none;">
297
+ <div class="spinner"></div>
298
+ <strong>DeepEngineer is analyzing your request...</strong>
299
+ </div>
300
+ """,
301
+ visible=False
302
+ )
303
+
304
+ # Log output section
305
+ with gr.Row():
306
+ with gr.Column(scale=1):
307
+ gr.HTML("""
308
+ <div class="log-container">
309
+ <div class="log-header">
310
+ <span class="log-icon">📊</span>
311
+ <h3 class="log-title">Analysis Progress</h3>
312
+ </div>
313
+ </div>
314
+ """)
315
+ log_output = gr.HTML(
316
+ value="<div class='log-text'>Ready to analyze your hardware engineering query...</div>",
317
+ elem_classes=["log-text"]
318
+ )
319
+
320
+ # Agent response section
321
+ with gr.Row():
322
+ with gr.Column(scale=1):
323
+ gr.HTML("""
324
+ <div class="response-container">
325
+ <h3>🎯 Analysis Results</h3>
326
+ </div>
327
+ """)
328
+ agent_output = gr.Markdown(
329
+ value="Enter your query above to begin the analysis.",
330
+ elem_classes=["response-container"]
331
+ )
332
+
333
+ # Event handlers
334
+ def on_send_click(user_input):
335
+ if not user_input.strip():
336
+ return "", "", gr.HTML(visible=False)
337
+
338
+ # Show processing indicator
339
+ processing_html = """
340
+ <div class="processing-indicator">
341
+ <div class="spinner"></div>
342
+ <strong>DeepEngineer is analyzing your request...</strong>
343
+ </div>
344
+ """
345
+ return "", "", gr.HTML(processing_html, visible=True)
346
+
347
+ def on_generate_complete(agent_result, log_result):
348
+ # Hide processing indicator when complete
349
+ return agent_result, log_result, gr.HTML(visible=False)
350
+
351
+ # Connect the button click
352
+ send_button.click(
353
+ fn=on_send_click,
354
+ inputs=[user_input],
355
+ outputs=[agent_output, log_output, processing_indicator]
356
+ ).then(
357
+ fn=run_agent_with_ui,
358
  inputs=[user_input],
359
  outputs=[agent_output, log_output],
360
+ concurrency_limit=1
361
+ ).then(
362
+ fn=on_generate_complete,
363
+ inputs=[agent_output, log_output],
364
+ outputs=[agent_output, log_output, processing_indicator]
365
  )
366
 
367
  if __name__ == "__main__":
368
+ demo.launch(
369
+ allowed_paths=[DATA_DIR],
370
+ server_name="0.0.0.0",
371
+ server_port=7860,
372
+ share=False
373
+ )