Spaces:
Sleeping
Sleeping
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- app.py +34 -20
- core/ui/ui.py +13 -5
- session_helpers.py +45 -0
- 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 |
-
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 164 |
-
|
| 165 |
-
|
|
|
|
| 166 |
|
| 167 |
-
# 2) Re-render the list of active agents
|
| 168 |
-
|
| 169 |
-
|
|
|
|
| 170 |
else:
|
| 171 |
active_md = "### 🧠 Active Agents\n_(None yet)_"
|
| 172 |
|
| 173 |
-
# 3) Build the new dropdown state
|
| 174 |
-
new_choices =
|
| 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 |
-
|
|
|
|
|
|
|
| 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
|
| 302 |
-
agent_json =
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
return "### 🧠 Active Agents\n_(None yet)_", []
|
| 20 |
output = "### 🧠 Active Agents\n"
|
| 21 |
dropdown_choices = []
|
| 22 |
-
for name, agent_json in
|
| 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 |
-
|
|
|
|
| 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__":
|