Browse Source

feat: split agent configs into modals

zhenyu.hu 3 weeks ago
parent
commit
add1ef309e

+ 25 - 0
docs/plans/todo-20-agent-config-modals.md

@@ -0,0 +1,25 @@
+# Todo 20 Agent Config Modals Plan
+
+**Status:** done
+
+## Goal
+
+Move ChatAgent and EventAgent settings out of the crowded sidebar into two separate modal dialogs.
+
+## Scope
+
+- Keep the existing request payload field ids and JavaScript data flow.
+- Add separate ChatAgent and EventAgent config entry buttons.
+- Move each agent's fields into its own `<dialog>`.
+- Keep Tools visible in the sidebar.
+
+## Verification
+
+- Static tests assert both modal dialogs, open buttons, close buttons, and JavaScript modal calls exist.
+- `uv run pytest` passes.
+
+## Evaluation
+
+- The sidebar now focuses on tools and agent-config entry points.
+- ChatAgent and EventAgent no longer compete for vertical space in the left panel.
+- Existing prompt-set save/load and WebSocket request building continue to use the same field ids.

+ 1 - 0
docs/plans/todos.md

@@ -46,3 +46,4 @@
 | 17 | done | `docs/plans/todo-17-agent-config-audit-tools.md` | Align ChatAgent/EventAgent config, add extra_body defaults, default EventAgent to one event round, add mock tools, audit events, and backend logging. | `uv run pytest` passes (`42 passed`, one existing Starlette deprecation warning). |
 | 18 | done | `docs/plans/todo-18-prompt-modal-tool-ui.md` | Move prompt workspace into a modal and improve visible tool-management UI. | `uv run pytest` passes (`46 passed`), plus local browser check confirms modal and tool UI render. |
 | 19 | done | `docs/plans/todo-19-latency-oriented-polish.md` | Do one optimization pass focused on reducing perceived reply wait time and cleaning frontend/backend rough edges. | `uv run pytest` passes (`49 passed`), plus local browser check confirms tools are visible in the first viewport. |
+| 20 | done | `docs/plans/todo-20-agent-config-modals.md` | Split ChatAgent and EventAgent settings into separate configuration modals so the sidebar is no longer crowded. | `uv run pytest` passes. |

+ 20 - 0
src/agent_lab/presentation/static/app.js

@@ -11,6 +11,8 @@ const preMessageTemplate = document.querySelector("#pre-message-template");
 const toolList = document.querySelector("#tool-list");
 const toolCount = document.querySelector("#tool-count");
 const promptDialog = document.querySelector("#prompt-dialog");
+const chatConfigDialog = document.querySelector("#chat-config-dialog");
+const eventConfigDialog = document.querySelector("#event-config-dialog");
 const PROMPT_SETS_STORAGE_KEY = "agent-lab.prompt-sets.v1";
 
 let socket = null;
@@ -38,6 +40,16 @@ document.querySelector("#open-prompt-config").addEventListener("click", () => {
 document.querySelector("#close-prompt-config").addEventListener("click", () => {
   promptDialog.close();
 });
+document.querySelector("#open-chat-config").addEventListener("click", openChatConfig);
+document.querySelector("#open-chat-config-panel").addEventListener("click", openChatConfig);
+document.querySelector("#close-chat-config").addEventListener("click", () => {
+  chatConfigDialog.close();
+});
+document.querySelector("#open-event-config").addEventListener("click", openEventConfig);
+document.querySelector("#open-event-config-panel").addEventListener("click", openEventConfig);
+document.querySelector("#close-event-config").addEventListener("click", () => {
+  eventConfigDialog.close();
+});
 document.querySelector("#select-all-tools").addEventListener("click", () => {
   setAllTools(true);
 });
@@ -53,6 +65,14 @@ chatForm.addEventListener("submit", (event) => {
 refreshPromptSetSelector();
 loadTools();
 
+function openChatConfig() {
+  chatConfigDialog.showModal();
+}
+
+function openEventConfig() {
+  eventConfigDialog.showModal();
+}
+
 async function loadTools() {
   try {
     const response = await fetch("/api/tools");

+ 50 - 26
src/agent_lab/presentation/static/index.html

@@ -11,6 +11,8 @@
       <h1>Agent Lab</h1>
       <div class="top-actions">
         <button id="open-prompt-config" type="button">Prompt Config</button>
+        <button id="open-chat-config" type="button">Chat Config</button>
+        <button id="open-event-config" type="button">Event Config</button>
         <div id="connection-status" class="status">Idle</div>
       </div>
     </header>
@@ -29,32 +31,11 @@
         </section>
 
         <section>
-          <h2>Chat Agent</h2>
-          <label>Model <input id="model" placeholder="Backend default" /></label>
-          <label>Temperature <input id="temperature" type="number" min="0" max="2" step="0.1" value="0.2" /></label>
-          <label>Max Tokens <input id="max-tokens" type="number" min="1" step="1" value="800" /></label>
-          <label>System Prompt <textarea id="chat-system-prompt" rows="4" placeholder="Optional ChatAgent system prompt"></textarea></label>
-          <fieldset>
-            <legend>Extra Body</legend>
-            <label class="checkbox"><input id="chat-thinking-disabled" type="checkbox" checked /> Disable thinking</label>
-            <label class="checkbox"><input id="chat-enable-search" type="checkbox" /> Enable search</label>
-            <label class="checkbox"><input id="chat-forced-search" type="checkbox" /> Forced search</label>
-          </fieldset>
-        </section>
-
-        <section>
-          <h2>Event Agent</h2>
-          <label>Model <input id="event-model" placeholder="Same as ChatAgent" /></label>
-          <label>Temperature <input id="event-temperature" type="number" min="0" max="2" step="0.1" value="0.2" /></label>
-          <label>Max Tokens <input id="event-max-tokens" type="number" min="1" step="1" value="800" /></label>
-          <label>System Prompt <textarea id="event-system-prompt" rows="4" placeholder="Optional EventAgent system prompt"></textarea></label>
-          <label>Max Loops <input id="max-event-loops" type="number" min="1" step="1" value="1" /></label>
-          <fieldset>
-            <legend>Extra Body</legend>
-            <label class="checkbox"><input id="event-thinking-disabled" type="checkbox" checked /> Disable thinking</label>
-            <label class="checkbox"><input id="event-enable-search" type="checkbox" /> Enable search</label>
-            <label class="checkbox"><input id="event-forced-search" type="checkbox" /> Forced search</label>
-          </fieldset>
+          <h2>Agent Config</h2>
+          <div class="button-row two">
+            <button id="open-chat-config-panel" type="button">Chat Agent</button>
+            <button id="open-event-config-panel" type="button">Event Agent</button>
+          </div>
         </section>
 
       </aside>
@@ -116,6 +97,49 @@
       </form>
     </dialog>
 
+    <dialog id="chat-config-dialog" class="modal">
+      <form method="dialog" class="modal-shell">
+        <div class="modal-head">
+          <h2>Chat Agent Config</h2>
+          <button id="close-chat-config" type="button">Close</button>
+        </div>
+        <section>
+          <label>Model <input id="model" placeholder="Backend default" /></label>
+          <label>Temperature <input id="temperature" type="number" min="0" max="2" step="0.1" value="0.2" /></label>
+          <label>Max Tokens <input id="max-tokens" type="number" min="1" step="1" value="800" /></label>
+          <label>System Prompt <textarea id="chat-system-prompt" rows="5" placeholder="Optional ChatAgent system prompt"></textarea></label>
+          <fieldset>
+            <legend>Extra Body</legend>
+            <label class="checkbox"><input id="chat-thinking-disabled" type="checkbox" checked /> Disable thinking</label>
+            <label class="checkbox"><input id="chat-enable-search" type="checkbox" /> Enable search</label>
+            <label class="checkbox"><input id="chat-forced-search" type="checkbox" /> Forced search</label>
+          </fieldset>
+        </section>
+      </form>
+    </dialog>
+
+    <dialog id="event-config-dialog" class="modal">
+      <form method="dialog" class="modal-shell">
+        <div class="modal-head">
+          <h2>Event Agent Config</h2>
+          <button id="close-event-config" type="button">Close</button>
+        </div>
+        <section>
+          <label>Model <input id="event-model" placeholder="Same as ChatAgent" /></label>
+          <label>Temperature <input id="event-temperature" type="number" min="0" max="2" step="0.1" value="0.2" /></label>
+          <label>Max Tokens <input id="event-max-tokens" type="number" min="1" step="1" value="800" /></label>
+          <label>System Prompt <textarea id="event-system-prompt" rows="5" placeholder="Optional EventAgent system prompt"></textarea></label>
+          <label>Max Loops <input id="max-event-loops" type="number" min="1" step="1" value="1" /></label>
+          <fieldset>
+            <legend>Extra Body</legend>
+            <label class="checkbox"><input id="event-thinking-disabled" type="checkbox" checked /> Disable thinking</label>
+            <label class="checkbox"><input id="event-enable-search" type="checkbox" /> Enable search</label>
+            <label class="checkbox"><input id="event-forced-search" type="checkbox" /> Forced search</label>
+          </fieldset>
+        </section>
+      </form>
+    </dialog>
+
     <template id="pre-message-template">
       <div class="pre-message">
         <select class="pre-role">

+ 7 - 1
src/agent_lab/presentation/static/styles.css

@@ -43,8 +43,8 @@ button:disabled {
   background: #ffffff;
   border-bottom: 1px solid #d7dee5;
   display: flex;
-  height: 56px;
   justify-content: space-between;
+  min-height: 56px;
   padding: 0 20px;
 }
 
@@ -56,7 +56,9 @@ button:disabled {
 .top-actions {
   align-items: center;
   display: flex;
+  flex-wrap: wrap;
   gap: 10px;
+  justify-content: flex-end;
 }
 
 .status {
@@ -122,6 +124,10 @@ h2 {
   margin-top: 0;
 }
 
+.button-row.two {
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
 label {
   display: grid;
   gap: 6px;

+ 13 - 0
tests/test_websocket_api.py

@@ -501,6 +501,19 @@ def test_static_agent_config_controls_are_aligned_for_chat_and_event_agents():
     html = Path("src/agent_lab/presentation/static/index.html").read_text()
     js = Path("src/agent_lab/presentation/static/app.js").read_text()
 
+    assert '<dialog id="chat-config-dialog"' in html
+    assert '<dialog id="event-config-dialog"' in html
+    assert 'id="open-chat-config"' in html
+    assert 'id="open-event-config"' in html
+    assert 'id="open-chat-config-panel"' in html
+    assert 'id="open-event-config-panel"' in html
+    assert 'id="close-chat-config"' in html
+    assert 'id="close-event-config"' in html
+    assert "chatConfigDialog.showModal()" in js
+    assert "eventConfigDialog.showModal()" in js
+    assert "chatConfigDialog.close()" in js
+    assert "eventConfigDialog.close()" in js
+
     for control_id in [
         "model",
         "temperature",