Просмотр исходного кода

feat: refine prompt list message types

zhenyu.hu 3 недель назад
Родитель
Сommit
b467a1af89

+ 23 - 0
docs/plans/todo-25-prompt-list-message-types.md

@@ -0,0 +1,23 @@
+# Todo 25: Prompt List Message Types
+
+## Status
+
+done
+
+## Goal
+
+The ChatAgent prompt list should be easier to scan and each item should clearly show its message type. For this iteration, prompt list items remain ChatAgent `system` messages; the UI must make that explicit instead of only showing "Custom Prompt".
+
+## Scope
+
+- Add a visible `type: system` marker to each ChatAgent prompt item.
+- Keep the generated EventAgent event-rules prompt in the same ordered list.
+- Make the item header, action buttons, and text area more compact inside the modal.
+- Preserve existing prompt save/load, add/delete, and drag ordering behavior.
+
+## Verification
+
+- Static tests assert that prompt items expose message type markup and compact styling hooks.
+- `uv run pytest` passes: 51 tests.
+- Local HTTP check returned `/health` and `/static/app.js?v=20260706-prompt-types`.
+- Browser automation was attempted but the in-app browser connection timed out, and direct Playwright did not have a browser binary installed in this environment.

+ 1 - 0
docs/plans/todos.md

@@ -51,3 +51,4 @@
 | 22 | done | `docs/plans/todo-22-text-event-protocol.md` | Replace ChatAgent tool calls with streamed text event parsing, inject generated event descriptions as a system message, and harden Agent config buttons. | `uv run pytest` passes (`50 passed`, one existing Starlette deprecation warning), plus local browser click check. |
 | 22 | done | `docs/plans/todo-22-text-event-protocol.md` | Replace ChatAgent tool calls with streamed text event parsing, inject generated event descriptions as a system message, and harden Agent config buttons. | `uv run pytest` passes (`50 passed`, one existing Starlette deprecation warning), plus local browser click check. |
 | 23 | done | `docs/plans/todo-23-agent-prompt-and-event-llm.md` | Move Prompt Config into ChatAgent config, show available-event prompt by default, and make EventAgent use LLM tool calls to generate tool arguments. | `uv run pytest` passes (`51 passed`, one existing Starlette deprecation warning); changed Python modules compile; local server returned page/static assets/tools API. |
 | 23 | done | `docs/plans/todo-23-agent-prompt-and-event-llm.md` | Move Prompt Config into ChatAgent config, show available-event prompt by default, and make EventAgent use LLM tool calls to generate tool arguments. | `uv run pytest` passes (`51 passed`, one existing Starlette deprecation warning); changed Python modules compile; local server returned page/static assets/tools API. |
 | 24 | done | `docs/plans/todo-24-chat-agent-prompt-list.md` | Put all ChatAgent prompts, including generated EventAgent event rules, into one ordered prompt list with add/delete/drag controls. | `uv run pytest` passes (`51 passed`, one existing Starlette deprecation warning), plus local browser prompt-list check. |
 | 24 | done | `docs/plans/todo-24-chat-agent-prompt-list.md` | Put all ChatAgent prompts, including generated EventAgent event rules, into one ordered prompt list with add/delete/drag controls. | `uv run pytest` passes (`51 passed`, one existing Starlette deprecation warning), plus local browser prompt-list check. |
+| 25 | done | `docs/plans/todo-25-prompt-list-message-types.md` | Make the ChatAgent prompt list visually compact and show the message type for each prompt item. | `uv run pytest` passes (`51 passed`, one existing Starlette deprecation warning); local service returned `/health` and versioned JS asset. |

+ 18 - 5
src/agent_lab/presentation/static/app.js

@@ -307,15 +307,25 @@ function createSystemPrompt(prompt) {
     ? "prompt-item event-prompt"
     ? "prompt-item event-prompt"
     : "prompt-item";
     : "prompt-item";
   item.dataset.promptKind = promptItem.prompt_kind;
   item.dataset.promptKind = promptItem.prompt_kind;
+  item.dataset.promptRole = promptItem.role;
   item.draggable = true;
   item.draggable = true;
 
 
   const head = document.createElement("div");
   const head = document.createElement("div");
   head.className = "prompt-item-head";
   head.className = "prompt-item-head";
 
 
+  const meta = document.createElement("div");
+  meta.className = "prompt-meta";
+
+  const type = document.createElement("span");
+  type.className = "prompt-type";
+  type.textContent = `type: ${promptItem.role}`;
+
   const title = document.createElement("span");
   const title = document.createElement("span");
+  title.className = "prompt-title";
   title.textContent = promptItem.prompt_kind === "event_agent_system"
   title.textContent = promptItem.prompt_kind === "event_agent_system"
-    ? "EventAgent System Prompt"
-    : "Custom Prompt";
+    ? "EventAgent Rules"
+    : "Custom";
+  meta.append(type, title);
 
 
   const actions = document.createElement("div");
   const actions = document.createElement("div");
   actions.className = "prompt-actions";
   actions.className = "prompt-actions";
@@ -342,11 +352,11 @@ function createSystemPrompt(prompt) {
     actions.append(deleteButton);
     actions.append(deleteButton);
   }
   }
 
 
-  head.append(title, actions);
+  head.append(meta, actions);
 
 
   const textarea = document.createElement("textarea");
   const textarea = document.createElement("textarea");
   textarea.className = "system-prompt";
   textarea.className = "system-prompt";
-  textarea.rows = promptItem.prompt_kind === "event_agent_system" ? 7 : 4;
+  textarea.rows = promptItem.prompt_kind === "event_agent_system" ? 7 : 3;
   textarea.value = promptItem.content;
   textarea.value = promptItem.content;
   textarea.readOnly = promptItem.prompt_kind === "event_agent_system";
   textarea.readOnly = promptItem.prompt_kind === "event_agent_system";
 
 
@@ -389,6 +399,7 @@ function buildPromptItems() {
   return [...systemPrompts.querySelectorAll(".prompt-item")]
   return [...systemPrompts.querySelectorAll(".prompt-item")]
     .map((item) => ({
     .map((item) => ({
       prompt_kind: item.dataset.promptKind || "custom",
       prompt_kind: item.dataset.promptKind || "custom",
+      role: item.dataset.promptRole || "system",
       content: item.querySelector(".system-prompt").value.trim(),
       content: item.querySelector(".system-prompt").value.trim(),
     }))
     }))
     .filter((item) => item.prompt_kind === "event_agent_system" || item.content);
     .filter((item) => item.prompt_kind === "event_agent_system" || item.content);
@@ -413,7 +424,7 @@ function normalizePromptItems(prompts) {
     normalized.push({ prompt_kind: "event_agent_system", content: "" });
     normalized.push({ prompt_kind: "event_agent_system", content: "" });
   }
   }
   if (!normalized.some((prompt) => prompt.prompt_kind === "custom")) {
   if (!normalized.some((prompt) => prompt.prompt_kind === "custom")) {
-    normalized.unshift({ prompt_kind: "custom", content: "" });
+    normalized.unshift({ prompt_kind: "custom", role: "system", content: "" });
   }
   }
   return normalized;
   return normalized;
 }
 }
@@ -424,11 +435,13 @@ function normalizePromptItem(prompt) {
       prompt_kind: prompt.prompt_kind === "event_agent_system"
       prompt_kind: prompt.prompt_kind === "event_agent_system"
         ? "event_agent_system"
         ? "event_agent_system"
         : "custom",
         : "custom",
+      role: "system",
       content: prompt.content || "",
       content: prompt.content || "",
     };
     };
   }
   }
   return {
   return {
     prompt_kind: "custom",
     prompt_kind: "custom",
+    role: "system",
     content: prompt || "",
     content: prompt || "",
   };
   };
 }
 }

+ 12 - 6
src/agent_lab/presentation/static/index.html

@@ -90,20 +90,26 @@
             <button id="add-system-prompt" type="button">Add</button>
             <button id="add-system-prompt" type="button">Add</button>
           </div>
           </div>
           <div id="system-prompts" class="prompt-list stack">
           <div id="system-prompts" class="prompt-list stack">
-            <div class="prompt-item" draggable="true" data-prompt-kind="custom">
+            <div class="prompt-item" draggable="true" data-prompt-kind="custom" data-prompt-role="system">
               <div class="prompt-item-head">
               <div class="prompt-item-head">
-                <span>Custom Prompt</span>
+                <div class="prompt-meta">
+                  <span class="prompt-type">type: system</span>
+                  <span class="prompt-title">Custom</span>
+                </div>
                 <div class="prompt-actions">
                 <div class="prompt-actions">
                   <button class="move-prompt-up" type="button">Up</button>
                   <button class="move-prompt-up" type="button">Up</button>
                   <button class="move-prompt-down" type="button">Down</button>
                   <button class="move-prompt-down" type="button">Down</button>
                   <button class="delete-system-prompt" type="button">Delete</button>
                   <button class="delete-system-prompt" type="button">Delete</button>
                 </div>
                 </div>
               </div>
               </div>
-              <textarea class="system-prompt" rows="4">You are a debugging assistant.</textarea>
+              <textarea class="system-prompt" rows="3">You are a debugging assistant.</textarea>
             </div>
             </div>
-            <div class="prompt-item event-prompt" draggable="true" data-prompt-kind="event_agent_system">
+            <div class="prompt-item event-prompt" draggable="true" data-prompt-kind="event_agent_system" data-prompt-role="system">
               <div class="prompt-item-head">
               <div class="prompt-item-head">
-                <span>EventAgent System Prompt</span>
+                <div class="prompt-meta">
+                  <span class="prompt-type">type: system</span>
+                  <span class="prompt-title">EventAgent Rules</span>
+                </div>
                 <div class="prompt-actions">
                 <div class="prompt-actions">
                   <button class="move-prompt-up" type="button">Up</button>
                   <button class="move-prompt-up" type="button">Up</button>
                   <button class="move-prompt-down" type="button">Down</button>
                   <button class="move-prompt-down" type="button">Down</button>
@@ -157,6 +163,6 @@
       </div>
       </div>
     </template>
     </template>
 
 
-    <script src="/static/app.js?v=20260706-prompt-list"></script>
+    <script src="/static/app.js?v=20260706-prompt-types"></script>
   </body>
   </body>
 </html>
 </html>

+ 43 - 6
src/agent_lab/presentation/static/styles.css

@@ -178,10 +178,11 @@ textarea {
 }
 }
 
 
 .prompt-item {
 .prompt-item {
+  background: #fbfcfd;
   border: 1px solid #d7dee5;
   border: 1px solid #d7dee5;
   border-radius: 6px;
   border-radius: 6px;
   cursor: grab;
   cursor: grab;
-  padding: 10px;
+  padding: 8px;
 }
 }
 
 
 .prompt-item.dragging {
 .prompt-item.dragging {
@@ -191,25 +192,61 @@ textarea {
 .prompt-item-head {
 .prompt-item-head {
   align-items: center;
   align-items: center;
   display: flex;
   display: flex;
-  gap: 10px;
+  gap: 8px;
   justify-content: space-between;
   justify-content: space-between;
-  margin-bottom: 8px;
+  margin-bottom: 6px;
 }
 }
 
 
-.prompt-item-head span {
+.prompt-meta {
+  align-items: center;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+  min-width: 0;
+}
+
+.prompt-title {
   color: #52616f;
   color: #52616f;
   font-size: 12px;
   font-size: 12px;
   font-weight: 600;
   font-weight: 600;
 }
 }
 
 
+.prompt-type {
+  background: #e0f2f1;
+  border: 1px solid #b2dfdb;
+  border-radius: 999px;
+  color: #0f766e;
+  font-size: 11px;
+  font-weight: 700;
+  line-height: 1;
+  padding: 3px 7px;
+  text-transform: lowercase;
+}
+
 .prompt-actions {
 .prompt-actions {
   display: flex;
   display: flex;
   flex-wrap: wrap;
   flex-wrap: wrap;
-  gap: 6px;
+  gap: 4px;
+  justify-content: flex-end;
 }
 }
 
 
 .prompt-actions button {
 .prompt-actions button {
-  padding: 5px 8px;
+  background: #eef2f5;
+  border: 1px solid #cbd5df;
+  color: #1f2933;
+  font-size: 12px;
+  line-height: 1.1;
+  padding: 4px 7px;
+}
+
+.prompt-actions button:hover {
+  background: #e4e9ee;
+}
+
+.prompt-item textarea {
+  font-size: 13px;
+  line-height: 1.45;
+  padding: 7px;
 }
 }
 
 
 .event-prompt textarea {
 .event-prompt textarea {

+ 7 - 0
tests/test_websocket_api.py

@@ -484,18 +484,25 @@ def test_static_chat_agent_prompt_is_a_single_sortable_list_with_event_instructi
     assert 'id="chat-event-prompt-preview"' not in html
     assert 'id="chat-event-prompt-preview"' not in html
     assert 'class="prompt-list stack"' in html
     assert 'class="prompt-list stack"' in html
     assert 'class="prompt-item"' in html
     assert 'class="prompt-item"' in html
+    assert 'data-prompt-role="system"' in html
+    assert "prompt-type" in html
     assert "function buildAvailableEventsPrompt(" in js
     assert "function buildAvailableEventsPrompt(" in js
     assert "Available events:" in js
     assert "Available events:" in js
     assert "function updateEventPromptItem(" in js
     assert "function updateEventPromptItem(" in js
     assert "function createSystemPrompt(" in js
     assert "function createSystemPrompt(" in js
     assert "function movePromptItem(" in js
     assert "function movePromptItem(" in js
     assert 'draggable = true' in js
     assert 'draggable = true' in js
+    assert "dataset.promptRole" in js
+    assert "prompt-title" in js
     assert "delete-system-prompt" in js
     assert "delete-system-prompt" in js
     assert 'prompt_kind: "event_agent_system"' in js
     assert 'prompt_kind: "event_agent_system"' in js
     assert "system_prompts: collectSystemPrompts()" in js
     assert "system_prompts: collectSystemPrompts()" in js
     assert 'system_prompt: ""' in js
     assert 'system_prompt: ""' in js
     assert ".prompt-item" in css
     assert ".prompt-item" in css
     assert ".prompt-item.dragging" in css
     assert ".prompt-item.dragging" in css
+    assert ".prompt-meta" in css
+    assert ".prompt-type" in css
+    assert ".prompt-actions button" in css
     assert "#open-prompt-config" not in js
     assert "#open-prompt-config" not in js
     assert "promptDialog" not in js
     assert "promptDialog" not in js