Keeby-smilyai commited on
Commit
91bc418
Β·
verified Β·
1 Parent(s): c14c44a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -95
app.py CHANGED
@@ -1,5 +1,6 @@
1
- # app.py β€” REFACTORED & BACKWARD-COMPATIBLE VERSION
2
  import gradio as gr
 
3
  from backend import (
4
  # User Management
5
  create_user,
@@ -16,9 +17,6 @@ from backend import (
16
  import psutil
17
  import torch
18
 
19
- # Gradio's session state will manage user data for each session
20
- # We will use gr.State to hold session-specific data
21
-
22
  # --- UI HELPER FUNCTIONS ---
23
 
24
  def get_system_stats():
@@ -45,9 +43,8 @@ def format_projects_list(projects):
45
 
46
  def get_project_choices(user_id):
47
  """Fetches user projects and formats them for a dropdown."""
48
- if not user_id:
49
- return []
50
- projects = get_user_projects(user_id, limit=20) # Increase limit for dropdown
51
  return [(f"[{p['id']}] {p['title']}", p['id']) for p in projects] if projects else []
52
 
53
  # --- CORE LOGIC HANDLERS FOR UI ---
@@ -55,8 +52,7 @@ def get_project_choices(user_id):
55
  def login_and_setup(username, password, user_state):
56
  """Handles login/signup and prepares the main UI if successful."""
57
  user = get_user_by_username(username)
58
- user_id = None
59
- welcome_message = ""
60
 
61
  if user and verify_password(password, user['password_hash']):
62
  user_id = user['id']
@@ -67,27 +63,21 @@ def login_and_setup(username, password, user_state):
67
  user_id = new_user_id
68
  welcome_message = f"Account created! Welcome, {username}!"
69
  else:
70
- # Login failed and username is taken
71
  return user_state, gr.update(), gr.update(value="Login failed or username is taken.")
72
 
73
  if user_id:
74
  user_state['user_id'] = user_id
75
  user_state['username'] = username
76
-
77
  projects = get_user_projects(user_id)
78
- project_list_md = format_projects_list(projects)
79
- dropdown_choices = get_project_choices(user_id)
80
 
81
- # Hide login UI, show main UI, and populate it with data
82
  return (
83
  user_state,
84
- gr.update(visible=False), # Hide login UI
85
- gr.update(visible=True), # Show main UI
86
  gr.update(value=welcome_message),
87
- gr.update(value=project_list_md),
88
- gr.update(choices=dropdown_choices, value=None)
89
  )
90
- # This path is for the failed login/signup
91
  return user_state, gr.update(), gr.update()
92
 
93
 
@@ -95,167 +85,144 @@ def start_project_flow(prompt, user_state):
95
  """The complete, seamless flow for starting a new project."""
96
  user_id = user_state.get('user_id')
97
  if not user_id:
98
- return "Error: Not logged in.", "", "", None, None, gr.update()
99
 
100
- # 1. Create project in DB
101
  project_id = create_project(user_id, "New AI Project", prompt)
102
  if not project_id:
103
- return "Error: Could not create project.", "", "", None, None, gr.update()
104
 
105
- # 2. Queue the backend job
106
  queue_job(project_id, user_id, prompt)
107
 
108
- # 3. Refresh UI elements with the new project data
109
  projects = get_user_projects(user_id)
110
- project_list_md = format_projects_list(projects)
111
- dropdown_choices = get_project_choices(user_id)
112
-
113
- # 4. Return updates for ALL relevant components
114
  return (
115
- f"βœ… Project #{project_id} started! Switching to logs...", # Status message
116
- gr.update(value=""), # Clear the prompt box
117
- gr.update(value=project_list_md), # Update dashboard project list
118
- gr.update(choices=dropdown_choices, value=project_id), # Update dropdown and AUTO-SELECT the new project
119
- gr.Tabs(selected="logs_tab"), # Switch to the Logs tab
120
  )
121
 
122
  def refresh_all_projects(user_state):
123
  """Manually refresh project lists on both tabs."""
124
  user_id = user_state.get('user_id')
125
- if not user_id:
126
- return gr.update(), gr.update()
127
-
128
- projects = get_user_projects(user_id)
129
- project_list_md = format_projects_list(projects)
130
- dropdown_choices = get_project_choices(user_id)
131
 
132
- return gr.update(value=project_list_md), gr.update(choices=dropdown_choices)
 
 
 
 
 
 
 
 
 
133
 
134
- def get_live_logs(project_id):
135
- """Fetches logs for the selected project. Designed for live polling."""
136
  if not project_id:
137
- return "⬅️ Select a project from the dropdown to see its live logs."
138
-
 
 
139
  project = get_project(project_id)
140
  if not project or not project['logs']:
141
- return f"πŸ•’ Project #{project_id} is queued or starting... Logs will appear here shortly."
142
-
143
- return project['logs']
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
  # --- GRADIO UI DEFINITION ---
146
 
147
  with gr.Blocks(title="Code Agents Pro", theme=gr.themes.Soft()) as demo:
148
- # Session state to hold user_id and username
149
  user_state = gr.State({"user_id": None, "username": ""})
150
 
151
  gr.Markdown("# πŸ€– Code Agents Pro β€” Your Automated AI Software Team")
152
 
153
- # --- PAGE 1: LOGIN UI ---
154
  with gr.Group(visible=True) as login_ui:
155
- with gr.Row():
156
- gr.Markdown("### πŸ” Login or Sign Up")
157
  username_input = gr.Textbox(label="Username", placeholder="Choose a unique username")
158
  password_input = gr.Textbox(label="Password", type="password", placeholder="Enter your password")
159
  login_btn = gr.Button("Login / Sign Up", variant="primary")
160
  login_msg = gr.Markdown()
161
 
162
- # --- PAGE 2: MAIN APPLICATION UI (Initially Hidden) ---
163
  with gr.Group(visible=False) as main_ui_container:
164
  welcome_msg = gr.Markdown()
165
 
166
  with gr.Tabs() as tabs:
167
- # --- DASHBOARD TAB ---
168
  with gr.Tab("πŸš€ Dashboard", id="dashboard_tab"):
169
  with gr.Row():
170
  with gr.Column(scale=2):
171
  gr.Markdown("### ✨ Start a New Project")
172
- project_prompt = gr.Textbox(
173
- label="Describe your project goal in detail",
174
- lines=8,
175
- placeholder="e.g., 'Build a Python FastAPI server with a PostgreSQL database using SQLAlchemy. It needs a /users endpoint for creating and listing users.'"
176
- )
177
  start_btn = gr.Button("πŸš€ Start AI Team Work", variant="primary")
178
  status_msg = gr.Markdown()
179
-
180
  with gr.Column(scale=3):
181
  gr.Markdown("### πŸ“ Your Projects")
182
  refresh_all_btn = gr.Button("πŸ”„ Refresh Project List")
183
  projects_display = gr.Markdown("Login to see your projects.")
184
 
185
- # --- AGENT LOGS TAB ---
186
  with gr.Tab("🧠 Agent Logs", id="logs_tab"):
187
  gr.Markdown("### πŸ“œ View Agent Conversation Logs")
188
- project_selector = gr.Dropdown(
189
- label="Select Project to View",
190
- choices=[],
191
- interactive=True,
192
- )
193
- # --- THIS IS THE FIX ---
194
- # Replaced gr.Box with the backward-compatible gr.Group
195
  with gr.Group(elem_classes=["logs-container"]):
196
  logs_display = gr.Markdown("Logs will appear here in real-time.", elem_classes=["monospace"])
197
 
198
- # System stats monitor at the bottom
199
  ram_monitor = gr.Markdown()
200
 
201
  # ------------------------------ EVENT WIRING ------------------------------
202
-
203
- # 1. Login Action
204
  login_btn.click(
205
  fn=login_and_setup,
206
  inputs=[username_input, password_input, user_state],
207
  outputs=[user_state, login_ui, main_ui_container, welcome_msg, projects_display, project_selector]
208
  )
209
 
210
- # 2. Start Project Action (THE SEAMLESS FLOW)
211
  start_btn.click(
212
  fn=start_project_flow,
213
  inputs=[project_prompt, user_state],
214
  outputs=[status_msg, project_prompt, projects_display, project_selector, tabs]
215
  )
216
 
217
- # 3. Manual Refresh Action
218
  refresh_all_btn.click(
219
  fn=refresh_all_projects,
220
  inputs=[user_state],
221
  outputs=[projects_display, project_selector]
222
  )
223
 
224
- # 4. Action when dropdown value changes MANUALLY
 
 
225
  project_selector.change(
226
- fn=get_live_logs,
227
  inputs=project_selector,
228
  outputs=logs_display
229
  )
230
 
231
- # 5. LIVE POLLING for system stats and logs
232
- demo.load(fn=get_system_stats, outputs=ram_monitor, every=5)
233
  demo.load(
234
- fn=get_live_logs,
235
- inputs=[project_selector], # It will use the current value of the dropdown
236
- outputs=[logs_display],
237
- every=3 # Refresh logs every 3 seconds
238
  )
239
 
240
  # --- CSS STYLING ---
241
  gr.HTML("""
242
  <style>
243
- .logs-container {
244
- height: 600px;
245
- overflow-y: auto;
246
- border: 1px solid #E5E7EB;
247
- border-radius: 8px;
248
- padding: 8px;
249
- background-color: #F9FAFB;
250
- }
251
- .monospace {
252
- font-family: 'SF Mono', 'Consolas', 'Courier New', monospace;
253
- font-size: 14px;
254
- white-space: pre-wrap;
255
- word-wrap: break-word;
256
- }
257
  </style>
258
  """)
259
 
260
- # Use queue() for per-user state management, essential for multi-user apps
261
  demo.queue().launch(server_name="0.0.0.0", server_port=7860)
 
1
+ # app.py β€” FINAL VERSION WITH CUSTOM GENERATOR LOOPS (BACKWARD-COMPATIBLE)
2
  import gradio as gr
3
+ import time # Import the time module for sleeping
4
  from backend import (
5
  # User Management
6
  create_user,
 
17
  import psutil
18
  import torch
19
 
 
 
 
20
  # --- UI HELPER FUNCTIONS ---
21
 
22
  def get_system_stats():
 
43
 
44
  def get_project_choices(user_id):
45
  """Fetches user projects and formats them for a dropdown."""
46
+ if not user_id: return []
47
+ projects = get_user_projects(user_id, limit=20)
 
48
  return [(f"[{p['id']}] {p['title']}", p['id']) for p in projects] if projects else []
49
 
50
  # --- CORE LOGIC HANDLERS FOR UI ---
 
52
  def login_and_setup(username, password, user_state):
53
  """Handles login/signup and prepares the main UI if successful."""
54
  user = get_user_by_username(username)
55
+ user_id, welcome_message = None, ""
 
56
 
57
  if user and verify_password(password, user['password_hash']):
58
  user_id = user['id']
 
63
  user_id = new_user_id
64
  welcome_message = f"Account created! Welcome, {username}!"
65
  else:
 
66
  return user_state, gr.update(), gr.update(value="Login failed or username is taken.")
67
 
68
  if user_id:
69
  user_state['user_id'] = user_id
70
  user_state['username'] = username
 
71
  projects = get_user_projects(user_id)
 
 
72
 
 
73
  return (
74
  user_state,
75
+ gr.update(visible=False),
76
+ gr.update(visible=True),
77
  gr.update(value=welcome_message),
78
+ gr.update(value=format_projects_list(projects)),
79
+ gr.update(choices=get_project_choices(user_id), value=None)
80
  )
 
81
  return user_state, gr.update(), gr.update()
82
 
83
 
 
85
  """The complete, seamless flow for starting a new project."""
86
  user_id = user_state.get('user_id')
87
  if not user_id:
88
+ return "Error: Not logged in.", "", "", None, gr.update()
89
 
 
90
  project_id = create_project(user_id, "New AI Project", prompt)
91
  if not project_id:
92
+ return "Error: Could not create project.", "", "", None, gr.update()
93
 
 
94
  queue_job(project_id, user_id, prompt)
95
 
 
96
  projects = get_user_projects(user_id)
 
 
 
 
97
  return (
98
+ f"βœ… Project #{project_id} started! Switching to logs...",
99
+ gr.update(value=""),
100
+ gr.update(value=format_projects_list(projects)),
101
+ gr.update(choices=get_project_choices(user_id), value=project_id),
102
+ gr.Tabs(selected="logs_tab"),
103
  )
104
 
105
  def refresh_all_projects(user_state):
106
  """Manually refresh project lists on both tabs."""
107
  user_id = user_state.get('user_id')
108
+ if not user_id: return gr.update(), gr.update()
 
 
 
 
 
109
 
110
+ projects = get_user_projects(user_id)
111
+ return gr.update(value=format_projects_list(projects)), gr.update(choices=get_project_choices(user_id))
112
+
113
+ # --- CUSTOM GENERATOR LOOPS (THE FIX) ---
114
+
115
+ def system_stats_loop():
116
+ """A generator that yields system stats every 5 seconds, forever."""
117
+ while True:
118
+ yield get_system_stats()
119
+ time.sleep(5)
120
 
121
+ def stream_logs(project_id):
122
+ """A generator that streams logs for a selected project every 3 seconds."""
123
  if not project_id:
124
+ yield "⬅️ Select a project from the dropdown to see its live logs."
125
+ return # Stop the generator if no project is selected
126
+
127
+ # Yield an initial message immediately
128
  project = get_project(project_id)
129
  if not project or not project['logs']:
130
+ yield f"πŸ•’ Project #{project_id} is queued or starting... Logs will appear here shortly."
131
+
132
+ # Loop to continuously poll for new logs
133
+ while True:
134
+ project = get_project(project_id)
135
+ if project:
136
+ yield project['logs']
137
+ # If project is finished, stop polling to save resources
138
+ if project['status'] in ['completed', 'failed']:
139
+ break
140
+ else:
141
+ yield f"Error: Project #{project_id} not found."
142
+ break
143
+ time.sleep(3)
144
+
145
 
146
  # --- GRADIO UI DEFINITION ---
147
 
148
  with gr.Blocks(title="Code Agents Pro", theme=gr.themes.Soft()) as demo:
 
149
  user_state = gr.State({"user_id": None, "username": ""})
150
 
151
  gr.Markdown("# πŸ€– Code Agents Pro β€” Your Automated AI Software Team")
152
 
 
153
  with gr.Group(visible=True) as login_ui:
154
+ gr.Markdown("### πŸ” Login or Sign Up")
 
155
  username_input = gr.Textbox(label="Username", placeholder="Choose a unique username")
156
  password_input = gr.Textbox(label="Password", type="password", placeholder="Enter your password")
157
  login_btn = gr.Button("Login / Sign Up", variant="primary")
158
  login_msg = gr.Markdown()
159
 
 
160
  with gr.Group(visible=False) as main_ui_container:
161
  welcome_msg = gr.Markdown()
162
 
163
  with gr.Tabs() as tabs:
 
164
  with gr.Tab("πŸš€ Dashboard", id="dashboard_tab"):
165
  with gr.Row():
166
  with gr.Column(scale=2):
167
  gr.Markdown("### ✨ Start a New Project")
168
+ project_prompt = gr.Textbox(label="Describe your project goal in detail", lines=8, placeholder="e.g., 'Build a Python FastAPI server...'")
 
 
 
 
169
  start_btn = gr.Button("πŸš€ Start AI Team Work", variant="primary")
170
  status_msg = gr.Markdown()
 
171
  with gr.Column(scale=3):
172
  gr.Markdown("### πŸ“ Your Projects")
173
  refresh_all_btn = gr.Button("πŸ”„ Refresh Project List")
174
  projects_display = gr.Markdown("Login to see your projects.")
175
 
 
176
  with gr.Tab("🧠 Agent Logs", id="logs_tab"):
177
  gr.Markdown("### πŸ“œ View Agent Conversation Logs")
178
+ project_selector = gr.Dropdown(label="Select Project to View", choices=[], interactive=True)
 
 
 
 
 
 
179
  with gr.Group(elem_classes=["logs-container"]):
180
  logs_display = gr.Markdown("Logs will appear here in real-time.", elem_classes=["monospace"])
181
 
 
182
  ram_monitor = gr.Markdown()
183
 
184
  # ------------------------------ EVENT WIRING ------------------------------
185
+
 
186
  login_btn.click(
187
  fn=login_and_setup,
188
  inputs=[username_input, password_input, user_state],
189
  outputs=[user_state, login_ui, main_ui_container, welcome_msg, projects_display, project_selector]
190
  )
191
 
 
192
  start_btn.click(
193
  fn=start_project_flow,
194
  inputs=[project_prompt, user_state],
195
  outputs=[status_msg, project_prompt, projects_display, project_selector, tabs]
196
  )
197
 
 
198
  refresh_all_btn.click(
199
  fn=refresh_all_projects,
200
  inputs=[user_state],
201
  outputs=[projects_display, project_selector]
202
  )
203
 
204
+ # --- THE FIX IN ACTION ---
205
+ # When the dropdown changes (either by user or by the start_project_flow),
206
+ # it triggers the stream_logs generator, starting our custom loop.
207
  project_selector.change(
208
+ fn=stream_logs,
209
  inputs=project_selector,
210
  outputs=logs_display
211
  )
212
 
213
+ # Use a generator for the system stats loop, triggered on app load.
214
+ # This is the backward-compatible way to do a recurring event.
215
  demo.load(
216
+ fn=system_stats_loop,
217
+ outputs=ram_monitor
 
 
218
  )
219
 
220
  # --- CSS STYLING ---
221
  gr.HTML("""
222
  <style>
223
+ .logs-container { height: 600px; overflow-y: auto; border: 1px solid #E5E7EB; border-radius: 8px; padding: 8px; background-color: #F9FAFB; }
224
+ .monospace { font-family: 'SF Mono', 'Consolas', 'Courier New', monospace; font-size: 14px; white-space: pre-wrap; word-wrap: break-word; }
 
 
 
 
 
 
 
 
 
 
 
 
225
  </style>
226
  """)
227
 
 
228
  demo.queue().launch(server_name="0.0.0.0", server_port=7860)