IDAgents Developer commited on
Commit
66ffe7c
·
1 Parent(s): a12a672

Implement per-user agent builder isolation - Each user now sees only their own agents

Browse files
Files changed (4) hide show
  1. app.py +34 -20
  2. core/ui/ui.py +13 -5
  3. session_helpers.py +45 -0
  4. user_session_manager.py +1 -0
app.py CHANGED
@@ -36,6 +36,8 @@ from session_helpers import (
36
  get_user_simple_chat_history, set_user_simple_chat_history,
37
  get_user_builder_chat_histories, set_user_builder_chat_histories,
38
  get_user_deployed_chat_histories, set_user_deployed_chat_histories,
 
 
39
  get_current_username, log_user_access
40
  )
41
  from core.agents.agent_utils import linkify_citations, build_agent, load_prefilled, prepare_download, preload_demo_chat, _safe_title, extract_clinical_variables_from_history
@@ -147,9 +149,12 @@ def reset_chat(agent_json):
147
  active_children= []
148
  return chat_history, "", invocation_log, active_children
149
 
150
- def load_agent_to_builder(agent_name):
151
- if agent_name in agents_config:
152
- agent_data = json.loads(agents_config[agent_name])
 
 
 
153
  return (
154
  agent_data.get("agent_type", ""),
155
  agent_data.get("agent_name", ""),
@@ -159,19 +164,21 @@ def load_agent_to_builder(agent_name):
159
  else:
160
  return None, "", "", []
161
 
162
- def remove_selected_agent(agent_name: str):
163
- # 1) Remove from your in-memory store
164
- if agent_name in agents_config:
165
- del agents_config[agent_name]
 
166
 
167
- # 2) Re-render the list of active agents
168
- if agents_config:
169
- active_md = "### 🧠 Active Agents\n" + "\n".join(f"- {name}" for name in agents_config)
 
170
  else:
171
  active_md = "### 🧠 Active Agents\n_(None yet)_"
172
 
173
- # 3) Build the new dropdown state
174
- new_choices = list(agents_config.keys())
175
  # gr.update to reset selection (value=None) and update choices
176
  dropdown_update = gr.update(choices=new_choices, value=None)
177
 
@@ -211,8 +218,10 @@ def on_agent_type_change(selected_type, was_prefilled):
211
  # manual change: clear everything
212
  return skill_update, gr.update(value=""), gr.update(value=""), False
213
 
214
- def chat_selected_agent(agent_name):
215
- agent_json = agents_config.get(agent_name, "")
 
 
216
  if agent_json:
217
  # reset_chat returns 4 values:
218
  # (chat_history, cleared_input, invocation_log, active_children)
@@ -298,8 +307,8 @@ def chatpanel_handle(agent_name, user_text, request: gr.Request):
298
  histories = get_user_deployed_chat_histories(request)
299
  log_user_access(request, f"chatpanel_handle for agent {agent_name}")
300
 
301
- # 1) Look up the JSON you saved in agents_config
302
- agent_json = agents_config.get(agent_name)
303
  if not agent_json:
304
  return [], histories, "", ""
305
 
@@ -344,8 +353,10 @@ def chatpanel_handle(agent_name, user_text, request: gr.Request):
344
 
345
  return final_history, histories, "", final_invocation_log
346
 
347
- def refresh_chat_dropdown():
348
- return gr.update(choices=list(agents_config.keys()), value=None)
 
 
349
 
350
  def build_ui():
351
  # --- App Layout ---
@@ -2114,14 +2125,17 @@ def build_ui():
2114
  outputs=[agent_type, agent_name, agent_mission, skills, prefill_flag]
2115
  )
2116
  uploaded_files.upload(fn=handle_uploaded_files, inputs=[uploaded_files], outputs=[upload_alert, upload_alert])
2117
- def handle_generate(agent_type, agent_name, agent_mission, selected_skills, web_access, allow_fallback, uploaded_files, link1, link2, link3, link4, challenger_toggle):
2118
  # Accept challenger_toggle as an argument
 
2119
  agent_json = build_agent(agent_type, agent_name, agent_mission, selected_skills, web_access, allow_fallback, uploaded_files, link1, link2, link3, link4)
2120
  # Add challenger_enabled to the agent config JSON
2121
  agent_data = json.loads(agent_json)
2122
  agent_data["challenger_enabled"] = challenger_toggle
2123
  agent_json = json.dumps(agent_data)
2124
- agents_config[agent_name] = agent_json
 
 
2125
  return agent_json
2126
 
2127
  generate_button.click(
 
36
  get_user_simple_chat_history, set_user_simple_chat_history,
37
  get_user_builder_chat_histories, set_user_builder_chat_histories,
38
  get_user_deployed_chat_histories, set_user_deployed_chat_histories,
39
+ get_user_agents_config, set_user_agents_config,
40
+ save_user_agent, get_user_agent, remove_user_agent, get_user_agent_names,
41
  get_current_username, log_user_access
42
  )
43
  from core.agents.agent_utils import linkify_citations, build_agent, load_prefilled, prepare_download, preload_demo_chat, _safe_title, extract_clinical_variables_from_history
 
149
  active_children= []
150
  return chat_history, "", invocation_log, active_children
151
 
152
+ def load_agent_to_builder(agent_name, request: gr.Request):
153
+ """Load agent to builder - per-user isolation."""
154
+ agent_json = get_user_agent(request, agent_name)
155
+ if agent_json:
156
+ agent_data = json.loads(agent_json)
157
+ log_user_access(request, f"load_agent_to_builder: {agent_name}")
158
  return (
159
  agent_data.get("agent_type", ""),
160
  agent_data.get("agent_name", ""),
 
164
  else:
165
  return None, "", "", []
166
 
167
+ def remove_selected_agent(agent_name: str, request: gr.Request):
168
+ """Remove agent from user's configuration - per-user isolation."""
169
+ # Remove from user's session
170
+ removed = remove_user_agent(request, agent_name)
171
+ log_user_access(request, f"remove_agent: {agent_name}")
172
 
173
+ # 2) Re-render the list of active agents for THIS USER
174
+ user_agent_names = get_user_agent_names(request)
175
+ if user_agent_names:
176
+ active_md = "### 🧠 Active Agents\n" + "\n".join(f"- {name}" for name in user_agent_names)
177
  else:
178
  active_md = "### 🧠 Active Agents\n_(None yet)_"
179
 
180
+ # 3) Build the new dropdown state for THIS USER
181
+ new_choices = user_agent_names
182
  # gr.update to reset selection (value=None) and update choices
183
  dropdown_update = gr.update(choices=new_choices, value=None)
184
 
 
218
  # manual change: clear everything
219
  return skill_update, gr.update(value=""), gr.update(value=""), False
220
 
221
+ def chat_selected_agent(agent_name, request: gr.Request):
222
+ """Load agent for chat - per-user isolation."""
223
+ agent_json = get_user_agent(request, agent_name)
224
+ log_user_access(request, f"chat_selected_agent: {agent_name}")
225
  if agent_json:
226
  # reset_chat returns 4 values:
227
  # (chat_history, cleared_input, invocation_log, active_children)
 
307
  histories = get_user_deployed_chat_histories(request)
308
  log_user_access(request, f"chatpanel_handle for agent {agent_name}")
309
 
310
+ # 1) Look up the JSON from USER's agents, not global
311
+ agent_json = get_user_agent(request, agent_name)
312
  if not agent_json:
313
  return [], histories, "", ""
314
 
 
353
 
354
  return final_history, histories, "", final_invocation_log
355
 
356
+ def refresh_chat_dropdown(request: gr.Request):
357
+ """Refresh chat dropdown with user's agents only."""
358
+ user_agent_names = get_user_agent_names(request)
359
+ return gr.update(choices=user_agent_names, value=None)
360
 
361
  def build_ui():
362
  # --- App Layout ---
 
2125
  outputs=[agent_type, agent_name, agent_mission, skills, prefill_flag]
2126
  )
2127
  uploaded_files.upload(fn=handle_uploaded_files, inputs=[uploaded_files], outputs=[upload_alert, upload_alert])
2128
+ def handle_generate(agent_type, agent_name, agent_mission, selected_skills, web_access, allow_fallback, uploaded_files, link1, link2, link3, link4, challenger_toggle, request: gr.Request):
2129
  # Accept challenger_toggle as an argument
2130
+ # PER-USER AGENT STORAGE
2131
  agent_json = build_agent(agent_type, agent_name, agent_mission, selected_skills, web_access, allow_fallback, uploaded_files, link1, link2, link3, link4)
2132
  # Add challenger_enabled to the agent config JSON
2133
  agent_data = json.loads(agent_json)
2134
  agent_data["challenger_enabled"] = challenger_toggle
2135
  agent_json = json.dumps(agent_data)
2136
+ # Save to USER's session, not global
2137
+ save_user_agent(request, agent_name, agent_json)
2138
+ log_user_access(request, f"generated_agent: {agent_name}")
2139
  return agent_json
2140
 
2141
  generate_button.click(
core/ui/ui.py CHANGED
@@ -5,6 +5,9 @@ from core.agents.chat_orchestrator import simulate_agent_response_stream
5
  import logging
6
  from config import agents_config, skills_library, prefilled_agents
7
 
 
 
 
8
  def show_landing():
9
  return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
10
 
@@ -14,19 +17,24 @@ def show_builder():
14
  def show_chat():
15
  return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
16
 
17
- def build_active_agents_markdown_and_dropdown():
18
- if not agents_config:
 
 
 
 
19
  return "### 🧠 Active Agents\n_(None yet)_", []
20
  output = "### 🧠 Active Agents\n"
21
  dropdown_choices = []
22
- for name, agent_json in agents_config.items():
23
  agent_type = json.loads(agent_json)["agent_type"]
24
  output += f"- {name} ({agent_type})\n"
25
  dropdown_choices.append(name)
26
  return output, dropdown_choices
27
 
28
- def refresh_active_agents_widgets():
29
- md, dd = build_active_agents_markdown_and_dropdown()
 
30
  return gr.update(value=md), gr.update(choices=dd, value=None)
31
 
32
  # All other UI callback functions from app.py
 
5
  import logging
6
  from config import agents_config, skills_library, prefilled_agents
7
 
8
+ # Import session helpers for per-user agent storage
9
+ from session_helpers import get_user_agents_config
10
+
11
  def show_landing():
12
  return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
13
 
 
17
  def show_chat():
18
  return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
19
 
20
+ def build_active_agents_markdown_and_dropdown(request: gr.Request):
21
+ """Build active agents list - per-user isolation."""
22
+ # Get user's agents, not global
23
+ user_agents_config = get_user_agents_config(request)
24
+
25
+ if not user_agents_config:
26
  return "### 🧠 Active Agents\n_(None yet)_", []
27
  output = "### 🧠 Active Agents\n"
28
  dropdown_choices = []
29
+ for name, agent_json in user_agents_config.items():
30
  agent_type = json.loads(agent_json)["agent_type"]
31
  output += f"- {name} ({agent_type})\n"
32
  dropdown_choices.append(name)
33
  return output, dropdown_choices
34
 
35
+ def refresh_active_agents_widgets(request: gr.Request):
36
+ """Refresh active agents widgets - per-user isolation."""
37
+ md, dd = build_active_agents_markdown_and_dropdown(request)
38
  return gr.update(value=md), gr.update(choices=dd, value=None)
39
 
40
  # All other UI callback functions from app.py
session_helpers.py CHANGED
@@ -168,6 +168,51 @@ def wrap_load_history(original_func):
168
  return wrapper
169
 
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  def log_user_access(request: gr.Request, action: str):
172
  """Log user access for debugging/auditing."""
173
  username = get_username_from_request(request)
 
168
  return wrapper
169
 
170
 
171
+ def get_user_agents_config(request: gr.Request) -> Dict:
172
+ """Get agents configuration for current user."""
173
+ username = get_username_from_request(request)
174
+ return session_manager.get_user_data(username, SessionKeys.AGENTS_CONFIG, default={})
175
+
176
+
177
+ def set_user_agents_config(request: gr.Request, config: Dict) -> None:
178
+ """Set agents configuration for current user."""
179
+ username = get_username_from_request(request)
180
+ session_manager.set_user_data(username, SessionKeys.AGENTS_CONFIG, config)
181
+
182
+
183
+ def save_user_agent(request: gr.Request, agent_name: str, agent_json: str) -> None:
184
+ """Save an agent to the current user's configuration."""
185
+ username = get_username_from_request(request)
186
+ agents_config = get_user_agents_config(request)
187
+ agents_config[agent_name] = agent_json
188
+ set_user_agents_config(request, agents_config)
189
+ logger.info(f"User '{username}' saved agent: {agent_name}")
190
+
191
+
192
+ def get_user_agent(request: gr.Request, agent_name: str) -> str:
193
+ """Get a specific agent from the current user's configuration."""
194
+ agents_config = get_user_agents_config(request)
195
+ return agents_config.get(agent_name, "")
196
+
197
+
198
+ def remove_user_agent(request: gr.Request, agent_name: str) -> bool:
199
+ """Remove an agent from the current user's configuration."""
200
+ username = get_username_from_request(request)
201
+ agents_config = get_user_agents_config(request)
202
+ if agent_name in agents_config:
203
+ del agents_config[agent_name]
204
+ set_user_agents_config(request, agents_config)
205
+ logger.info(f"User '{username}' removed agent: {agent_name}")
206
+ return True
207
+ return False
208
+
209
+
210
+ def get_user_agent_names(request: gr.Request) -> List[str]:
211
+ """Get list of agent names for the current user."""
212
+ agents_config = get_user_agents_config(request)
213
+ return list(agents_config.keys())
214
+
215
+
216
  def log_user_access(request: gr.Request, action: str):
217
  """Log user access for debugging/auditing."""
218
  username = get_username_from_request(request)
user_session_manager.py CHANGED
@@ -182,6 +182,7 @@ class SessionKeys:
182
  PATIENT_DATA = "patient_data"
183
  AGENT_OUTPUT = "agent_output"
184
  PREFILL_FLAG = "prefill_flag"
 
185
 
186
 
187
  if __name__ == "__main__":
 
182
  PATIENT_DATA = "patient_data"
183
  AGENT_OUTPUT = "agent_output"
184
  PREFILL_FLAG = "prefill_flag"
185
+ AGENTS_CONFIG = "agents_config" # Per-user agent storage
186
 
187
 
188
  if __name__ == "__main__":