Keeby-smilyai commited on
Commit
39bc928
Β·
verified Β·
1 Parent(s): 6bdb2fa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -169
app.py CHANGED
@@ -1,236 +1,260 @@
 
1
  import gradio as gr
2
  from backend import (
 
3
  create_user,
4
  get_user_by_username,
5
  verify_password,
 
6
  create_project,
7
  get_user_projects,
8
  search_projects,
 
 
9
  queue_job,
10
- get_project
11
  )
12
  import psutil
13
  import torch
14
 
15
- # User state is managed per-session by Gradio's queue
16
- user_state = {"user_id": None, "username": ""}
 
17
 
18
- # --- Backend Logic Functions ---
19
 
20
- def page_login(username, password):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  user = get_user_by_username(username)
22
- if user and verify_password(password, user[1]):
23
- user_state["user_id"] = user[0]
24
- user_state["username"] = username
25
- return gr.update(visible=False), gr.update(visible=True), f"Welcome back, {username}! πŸ‘¨β€πŸ’»"
 
 
26
  else:
27
- # Try to create a new user
28
- user_id = create_user(username, password)
29
- if user_id:
30
- user_state["user_id"] = user_id
31
- user_state["username"] = username
32
- return gr.update(visible=False), gr.update(visible=True), f"Account created! Welcome, {username}!"
33
  else:
34
- return gr.update(), gr.update(), "Login failed or username is taken."
 
35
 
36
- def page_projects():
37
- if not user_state["user_id"]:
38
- return "Please login first.", ""
39
- projects = get_user_projects(user_state["user_id"])
40
- if not projects:
41
- return "No projects yet. Create one below!", ""
42
-
43
- project_list = "\n".join([
44
- f"πŸ“ [{p[0]}] {p[1]} ({p[3]}) - {p[5]}"
45
- for p in projects
46
- ])
47
- return project_list, ""
 
 
 
 
 
 
 
48
 
49
- def start_new_project(prompt):
50
- if not user_state["user_id"]:
51
- return "❌ Please login first.", ""
52
 
53
- projects = get_user_projects(user_state["user_id"])
54
- if len(projects) >= 3:
55
- return "⚠️ You can only have 3 projects. Delete one to create another.", ""
 
 
56
 
57
- project_id = create_project(user_state["user_id"], "New Project", prompt)
58
- queue_job(project_id, user_state["user_id"], prompt)
59
-
60
- status_message = f"βœ… Project #{project_id} started! Go to the 'Agent Conversation' tab and click 'Load My Projects' to see its logs."
 
 
 
61
 
62
- # Return updates for the UI
63
- return status_message, "" # Return message and clear prompt
64
-
65
- def search_projects_ui(query):
66
- if not user_state["user_id"]:
67
- return "Please login first."
68
- results = search_projects(user_state["user_id"], query)
69
- if not results:
70
- return "No matching projects."
71
- return "\n".join([f"πŸ“ [{p[0]}] {p[1]} ({p[3]})" for p in results])
72
-
73
- def update_dropdown():
74
- if not user_state["user_id"]:
75
- return gr.update(choices=[])
76
- projects = get_user_projects(user_state["user_id"], limit=10)
77
- choices = [(f"[{p[0]}] {p[1]}", p[0]) for p in projects]
78
- return gr.update(choices=choices)
 
 
 
 
 
 
79
 
80
- def show_agent_logs(project_id):
 
 
 
81
  if not project_id:
82
- return "Select a project from the dropdown to view its logs."
 
83
  project = get_project(project_id)
84
- if not project:
85
- return "Project not found."
86
- logs = project[6] # logs field
87
- if not logs:
88
- return "No logs generated yet. The agent might still be working..."
89
- return logs
90
-
91
- ## IMPROVEMENT: Create a dedicated function for system stats.
92
- def update_system_stats():
93
- ram_used = round(psutil.virtual_memory().used / (1024**3), 2)
94
- ram_total = round(psutil.virtual_memory().total / (1024**3), 2)
95
- stats_str = f"πŸ“Š System RAM: {ram_used}GB / {ram_total}GB"
96
- if torch.cuda.is_available():
97
- vram_used = round(torch.cuda.memory_reserved() / (1024**3), 2)
98
- vram_total = round(torch.cuda.get_device_properties(0).total_memory / (1024**3), 2)
99
- stats_str += f" | VRAM: {vram_used}GB / {vram_total}GB"
100
- return stats_str
101
 
102
- # --- Gradio UI ---
103
 
104
  with gr.Blocks(title="Code Agents Pro", theme=gr.themes.Soft()) as demo:
105
- gr.Markdown("# πŸ€– Code Agents Pro β€” AI Software Team, Fully Automated")
 
 
 
106
 
107
- # ---- PAGE 1: LOGIN ----
108
- with gr.Group() as page_login_ui:
109
  with gr.Row():
110
  gr.Markdown("### πŸ” Login or Sign Up")
111
  username_input = gr.Textbox(label="Username", placeholder="Choose a unique username")
112
- password_input = gr.Textbox(label="Password", type="password", placeholder="Minimum 6 characters")
113
  login_btn = gr.Button("Login / Sign Up", variant="primary")
114
  login_msg = gr.Markdown()
115
 
116
- # ---- PAGE 2: MAIN DASHBOARD ----
117
- with gr.Tab("πŸš€ Projects Dashboard", visible=False) as page_main_ui:
118
- with gr.Row():
119
- with gr.Column(scale=2):
120
- gr.Markdown("### βž• Start New Project")
121
- project_prompt = gr.Textbox(
122
- label="Describe your project goal",
123
- lines=5,
124
- placeholder="e.g., 'Build a Flask app for uploading and labeling images.'"
125
- )
126
- start_btn = gr.Button("πŸš€ Start AI Team Work", variant="primary")
127
- status_msg = gr.Markdown()
128
- with gr.Column(scale=3):
129
- gr.Markdown("### πŸ“ Your Projects (Max 3)")
130
- refresh_btn = gr.Button("πŸ”„ Refresh Projects")
131
- projects_display = gr.Markdown(label="Your Projects", value="Login to see your projects.")
132
- with gr.Accordion("πŸ” Search Projects", open=False):
133
- search_input = gr.Textbox(label="Search Query", placeholder="Type to search...")
134
- search_btn = gr.Button("Search")
135
- search_results = gr.Markdown(label="Search Results")
136
-
137
- ram_monitor = gr.Markdown() # Placeholder for system stats
138
-
139
- # ---- PAGE 3: AGENT CONVERSATION LOGS ----
140
- with gr.Tab("🧠 Agent Conversation", visible=False) as page_logs_ui:
141
- gr.Markdown("### πŸ“œ View Agent Logs")
142
- with gr.Row():
143
- project_selector = gr.Dropdown(
144
- label="Select Project to View Logs",
145
- choices=[],
146
- interactive=True,
147
- allow_custom_value=False,
148
- ## IMPROVEMENT: Your correct fix is here. This prevents auto-selection.
149
- value=None,
150
- )
151
- refresh_dropdown_btn = gr.Button("πŸ”„ Load My Projects")
152
 
153
- ## IMPROVEMENT: Use a styled gr.Box for better control over height and scrolling.
154
- with gr.Box(elem_classes=["logs-container"]):
155
- logs_display = gr.Markdown(
156
- label="Agent Messages",
157
- value="Select a project and click 'Load' to see the conversation.",
158
- elem_classes=["monospace"]
159
- )
160
-
161
- # ------------------------------ EVENT HANDLING ------------------------------
162
-
163
- # Login button action
164
- login_btn.click(
165
- page_login,
166
- inputs=[username_input, password_input],
167
- outputs=[page_login_ui, page_main_ui, login_msg]
168
- ).then(
169
- page_projects,
170
- outputs=[projects_display, search_results] # Also load projects on login
171
- )
172
 
173
- # Refresh buttons
174
- refresh_btn.click(
175
- page_projects,
176
- outputs=[projects_display, search_results] # Clears search results on refresh
177
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- refresh_dropdown_btn.click(
180
- fn=update_dropdown,
181
- outputs=project_selector
182
- )
183
 
184
- # Search button action
185
- search_btn.click(
186
- search_projects_ui,
187
- inputs=search_input,
188
- outputs=search_results
189
  )
190
-
191
- # Start new project button action
192
  start_btn.click(
193
- start_new_project,
194
- inputs=project_prompt,
195
- outputs=[status_msg, project_prompt] # Clear prompt after submission
196
- ).then(
197
- page_projects, # Refresh the main project list
198
- outputs=[projects_display, search_results]
199
- ).then(
200
- update_dropdown, # Also refresh the dropdown in the logs tab
201
- outputs=project_selector
202
  )
203
 
204
- # When a project is selected from dropdown, show its logs
 
 
 
 
 
 
 
205
  project_selector.change(
206
- fn=show_agent_logs,
207
  inputs=project_selector,
208
  outputs=logs_display
209
  )
210
 
211
- # --- Live Updates & Styling ---
212
-
213
- ## IMPROVEMENT: Update system stats every 5 seconds after the app loads.
214
- demo.load(update_system_stats, None, ram_monitor, every=5)
 
 
 
 
215
 
216
- ## IMPROVEMENT: CSS for modern scrolling container and monospace font.
217
  gr.HTML("""
218
  <style>
219
  .logs-container {
220
  height: 600px;
221
  overflow-y: auto;
222
- border: 1px solid #e0e0e0;
223
  border-radius: 8px;
 
 
224
  }
225
  .monospace {
226
  font-family: 'SF Mono', 'Consolas', 'Courier New', monospace;
227
  font-size: 14px;
228
  white-space: pre-wrap;
229
  word-wrap: break-word;
230
- padding: 10px;
231
  }
232
  </style>
233
  """)
234
 
235
- # Use queue() for per-user state management
236
  demo.queue().launch(server_name="0.0.0.0", server_port=7860)
 
1
+ # app.py β€” REFACTORED FOR A LIVE, SEAMLESS, AND ROBUST UI
2
  import gradio as gr
3
  from backend import (
4
+ # User Management
5
  create_user,
6
  get_user_by_username,
7
  verify_password,
8
+ # Project Management
9
  create_project,
10
  get_user_projects,
11
  search_projects,
12
+ get_project,
13
+ # Job Queue
14
  queue_job,
 
15
  )
16
  import psutil
17
  import torch
18
 
19
+ # Gradio's session state will manage user data for each session
20
+ # No global user_state dictionary needed for this approach
21
+ # We will use gr.State to hold session-specific data
22
 
23
+ # --- UI HELPER FUNCTIONS ---
24
 
25
+ def get_system_stats():
26
+ """Returns a markdown string with current RAM and VRAM usage."""
27
+ ram_used = round(psutil.virtual_memory().used / (1024**3), 2)
28
+ ram_total = round(psutil.virtual_memory().total / (1024**3), 2)
29
+ stats_str = f"πŸ“Š System RAM: {ram_used}GB / {ram_total}GB"
30
+ if torch.cuda.is_available():
31
+ vram_reserved = round(torch.cuda.memory_reserved() / (1024**3), 2)
32
+ vram_total = round(torch.cuda.get_device_properties(0).total_memory / (1024**3), 2)
33
+ stats_str += f" | VRAM: {vram_reserved}GB / {vram_total}GB"
34
+ return stats_str
35
+
36
+ def format_projects_list(projects):
37
+ """Formats a list of project records into a readable markdown string."""
38
+ if not projects:
39
+ return "No projects yet. Start one on the left!"
40
+
41
+ project_lines = []
42
+ for p in projects:
43
+ status_icon = {"queued": "πŸ•’", "running": "βš™οΈ", "completed": "βœ…", "failed": "❌"}.get(p['status'], "❓")
44
+ project_lines.append(f"{status_icon} **[{p['id']}] {p['title']}** ({p['status']})")
45
+ return "\n".join(project_lines)
46
+
47
+ def get_project_choices(user_id):
48
+ """Fetches user projects and formats them for a dropdown."""
49
+ if not user_id:
50
+ return []
51
+ projects = get_user_projects(user_id, limit=20) # Increase limit for dropdown
52
+ return [(f"[{p['id']}] {p['title']}", p['id']) for p in projects] if projects else []
53
+
54
+ # --- CORE LOGIC HANDLERS FOR UI ---
55
+
56
+ def login_and_setup(username, password, user_state):
57
+ """Handles login/signup and prepares the main UI if successful."""
58
  user = get_user_by_username(username)
59
+ user_id = None
60
+ welcome_message = ""
61
+
62
+ if user and verify_password(password, user['password_hash']):
63
+ user_id = user['id']
64
+ welcome_message = f"Welcome back, {username}! πŸ‘¨β€πŸ’»"
65
  else:
66
+ new_user_id = create_user(username, password)
67
+ if new_user_id:
68
+ user_id = new_user_id
69
+ welcome_message = f"Account created! Welcome, {username}!"
 
 
70
  else:
71
+ # Login failed and username is taken
72
+ return user_state, gr.update(), gr.update(value="Login failed or username is taken.")
73
 
74
+ if user_id:
75
+ user_state['user_id'] = user_id
76
+ user_state['username'] = username
77
+
78
+ projects = get_user_projects(user_id)
79
+ project_list_md = format_projects_list(projects)
80
+ dropdown_choices = get_project_choices(user_id)
81
+
82
+ # Hide login UI, show main UI, and populate it with data
83
+ return (
84
+ user_state,
85
+ gr.update(visible=False), # Hide login UI
86
+ gr.update(visible=True), # Show main UI
87
+ gr.update(value=welcome_message),
88
+ gr.update(value=project_list_md),
89
+ gr.update(choices=dropdown_choices, value=None)
90
+ )
91
+ # This path is for the failed login/signup
92
+ return user_state, gr.update(), gr.update()
93
 
 
 
 
94
 
95
+ def start_project_flow(prompt, user_state):
96
+ """The complete, seamless flow for starting a new project."""
97
+ user_id = user_state.get('user_id')
98
+ if not user_id:
99
+ return "Error: Not logged in.", "", "", None, None, gr.update()
100
 
101
+ # 1. Create project in DB
102
+ project_id = create_project(user_id, "New AI Project", prompt)
103
+ if not project_id:
104
+ return "Error: Could not create project.", "", "", None, None, gr.update()
105
+
106
+ # 2. Queue the backend job
107
+ queue_job(project_id, user_id, prompt)
108
 
109
+ # 3. Refresh UI elements with the new project data
110
+ projects = get_user_projects(user_id)
111
+ project_list_md = format_projects_list(projects)
112
+ dropdown_choices = get_project_choices(user_id)
113
+
114
+ # 4. Return updates for ALL relevant components
115
+ return (
116
+ f"βœ… Project #{project_id} started! Switching to logs...", # Status message
117
+ gr.update(value=""), # Clear the prompt box
118
+ gr.update(value=project_list_md), # Update dashboard project list
119
+ gr.update(choices=dropdown_choices, value=project_id), # Update dropdown and AUTO-SELECT the new project
120
+ gr.Tabs(selected="logs_tab"), # Switch to the Logs tab
121
+ )
122
+
123
+ def refresh_all_projects(user_state):
124
+ """Manually refresh project lists on both tabs."""
125
+ user_id = user_state.get('user_id')
126
+ if not user_id:
127
+ return gr.update(), gr.update()
128
+
129
+ projects = get_user_projects(user_id)
130
+ project_list_md = format_projects_list(projects)
131
+ dropdown_choices = get_project_choices(user_id)
132
 
133
+ return gr.update(value=project_list_md), gr.update(choices=dropdown_choices)
134
+
135
+ def get_live_logs(project_id):
136
+ """Fetches logs for the selected project. Designed for live polling."""
137
  if not project_id:
138
+ return "⬅️ Select a project from the dropdown to see its live logs."
139
+
140
  project = get_project(project_id)
141
+ if not project or not project['logs']:
142
+ return f"πŸ•’ Project #{project_id} is queued or starting... Logs will appear here shortly."
143
+
144
+ return project['logs']
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
+ # --- GRADIO UI DEFINITION ---
147
 
148
  with gr.Blocks(title="Code Agents Pro", theme=gr.themes.Soft()) as demo:
149
+ # Session state to hold user_id and username
150
+ user_state = gr.State({"user_id": None, "username": ""})
151
+
152
+ gr.Markdown("# πŸ€– Code Agents Pro β€” Your Automated AI Software Team")
153
 
154
+ # --- PAGE 1: LOGIN UI ---
155
+ with gr.Group(visible=True) as login_ui:
156
  with gr.Row():
157
  gr.Markdown("### πŸ” Login or Sign Up")
158
  username_input = gr.Textbox(label="Username", placeholder="Choose a unique username")
159
+ password_input = gr.Textbox(label="Password", type="password", placeholder="Enter your password")
160
  login_btn = gr.Button("Login / Sign Up", variant="primary")
161
  login_msg = gr.Markdown()
162
 
163
+ # --- PAGE 2: MAIN APPLICATION UI (Initially Hidden) ---
164
+ with gr.Group(visible=False) as main_ui_container:
165
+ welcome_msg = gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
+ with gr.Tabs() as tabs:
168
+ # --- DASHBOARD TAB ---
169
+ with gr.Tab("πŸš€ Dashboard", id="dashboard_tab"):
170
+ with gr.Row():
171
+ with gr.Column(scale=2):
172
+ gr.Markdown("### ✨ Start a New Project")
173
+ project_prompt = gr.Textbox(
174
+ label="Describe your project goal in detail",
175
+ lines=8,
176
+ placeholder="e.g., 'Build a Python FastAPI server with a PostgreSQL database using SQLAlchemy. It needs a /users endpoint for creating and listing users.'"
177
+ )
178
+ start_btn = gr.Button("πŸš€ Start AI Team Work", variant="primary")
179
+ status_msg = gr.Markdown()
 
 
 
 
 
 
180
 
181
+ with gr.Column(scale=3):
182
+ gr.Markdown("### πŸ“ Your Projects")
183
+ refresh_all_btn = gr.Button("πŸ”„ Refresh Project List")
184
+ projects_display = gr.Markdown("Login to see your projects.")
185
+
186
+ # --- AGENT LOGS TAB ---
187
+ with gr.Tab("🧠 Agent Logs", id="logs_tab"):
188
+ gr.Markdown("### πŸ“œ View Agent Conversation Logs")
189
+ project_selector = gr.Dropdown(
190
+ label="Select Project to View",
191
+ choices=[],
192
+ interactive=True,
193
+ )
194
+ with gr.Box(elem_classes=["logs-container"]):
195
+ logs_display = gr.Markdown("Logs will appear here in real-time.", elem_classes=["monospace"])
196
+
197
+ # System stats monitor at the bottom
198
+ ram_monitor = gr.Markdown()
199
 
200
+ # ------------------------------ EVENT WIRING ------------------------------
 
 
 
201
 
202
+ # 1. Login Action
203
+ login_btn.click(
204
+ fn=login_and_setup,
205
+ inputs=[username_input, password_input, user_state],
206
+ outputs=[user_state, login_ui, main_ui_container, welcome_msg, projects_display, project_selector]
207
  )
208
+
209
+ # 2. Start Project Action (THE SEAMLESS FLOW)
210
  start_btn.click(
211
+ fn=start_project_flow,
212
+ inputs=[project_prompt, user_state],
213
+ outputs=[status_msg, project_prompt, projects_display, project_selector, tabs]
 
 
 
 
 
 
214
  )
215
 
216
+ # 3. Manual Refresh Action
217
+ refresh_all_btn.click(
218
+ fn=refresh_all_projects,
219
+ inputs=[user_state],
220
+ outputs=[projects_display, project_selector]
221
+ )
222
+
223
+ # 4. Action when dropdown value changes MANUALLY
224
  project_selector.change(
225
+ fn=get_live_logs,
226
  inputs=project_selector,
227
  outputs=logs_display
228
  )
229
 
230
+ # 5. LIVE POLLING for system stats and logs
231
+ demo.load(fn=get_system_stats, outputs=ram_monitor, every=5)
232
+ demo.load(
233
+ fn=get_live_logs,
234
+ inputs=[project_selector], # It will use the current value of the dropdown
235
+ outputs=[logs_display],
236
+ every=3 # Refresh logs every 3 seconds
237
+ )
238
 
239
+ # --- CSS STYLING ---
240
  gr.HTML("""
241
  <style>
242
  .logs-container {
243
  height: 600px;
244
  overflow-y: auto;
245
+ border: 1px solid #E5E7EB;
246
  border-radius: 8px;
247
+ padding: 8px;
248
+ background-color: #F9FAFB;
249
  }
250
  .monospace {
251
  font-family: 'SF Mono', 'Consolas', 'Courier New', monospace;
252
  font-size: 14px;
253
  white-space: pre-wrap;
254
  word-wrap: break-word;
 
255
  }
256
  </style>
257
  """)
258
 
259
+ # Use queue() for per-user state management, essential for multi-user apps
260
  demo.queue().launch(server_name="0.0.0.0", server_port=7860)