summaryrefslogtreecommitdiff
path: root/Omni/Jr
diff options
context:
space:
mode:
authorBen Sima <ben@bensima.com>2025-12-01 07:50:29 -0500
committerBen Sima <ben@bensima.com>2025-12-01 07:50:29 -0500
commitd701ca93850a2572eeea4029a3d53cd3138e601b (patch)
tree9fea8ebb2a3bec76f1862c4c4158df7eb14b2e13 /Omni/Jr
parent4919cf825d4fdbcecc1f69fcf2a32176dfdde5ac (diff)
Fix agent event content double-encoding in web UI
Excellent! The changes have been successfully applied. Let me create a s The issue was that agent event content was being double-encoded in the w 1. **ToolResult events** showed raw JSON like `{"output":"Replaced 1 occ 2. **Assistant messages** showed literal `\n` instead of actual newlines - In `Omni/Agent/Engine.hs` (line 600), tool results are JSON-encoded wh - These JSON strings are stored as-is in the database via `insertAgentEv - The Web UI was displaying these JSON strings directly without decoding - Assistant messages contained literal `\n` escape sequences that weren' I modified `Omni/Jr/Web.hs` with the following changes: 1. **Added import**: `Data.Aeson.KeyMap` to work with JSON objects 2. **Created helper function `renderTextWithNewlines`** (line 2545-2553) - Splits text on literal `\n` sequences - Renders each part with `<br>` tags between them - Used in `renderAssistantEvent` to properly display newlines 3. **Created helper function `renderDecodedToolResult`** (line 2555-2563 - Attempts to decode JSON content - Extracts the `output` field from the JSON object - Falls back to raw content if parsing fails - Used in `renderToolResultEvent` to show clean output instead of raw 4. **Updated `renderAssistantEvent`** (line 2473): - Changed from `Lucid.toHtml truncated` to `renderTextWithNewlines tr 5. **Updated `renderToolResultEvent`** (lines 2502-2503): - Changed both occurrences from `Lucid.toHtml content` to `renderDeco The build now passes successfully with `bild --test Omni/Jr/Web.hs`. Task-Id: t-200
Diffstat (limited to 'Omni/Jr')
-rw-r--r--Omni/Jr/Web.hs27
1 files changed, 24 insertions, 3 deletions
diff --git a/Omni/Jr/Web.hs b/Omni/Jr/Web.hs
index d191454..aa8d4de 100644
--- a/Omni/Jr/Web.hs
+++ b/Omni/Jr/Web.hs
@@ -19,6 +19,7 @@ where
import Alpha
import qualified Control.Concurrent as Concurrent
import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.KeyMap as KeyMap
import qualified Data.ByteString.Lazy as LBS
import qualified Data.List as List
import qualified Data.Text as Text
@@ -2469,7 +2470,7 @@ renderAssistantEvent content timestamp now =
Lucid.div_ [Lucid.class_ "event-content event-bubble"] <| do
let truncated = Text.take 2000 content
isTruncated = Text.length content > 2000
- Lucid.toHtml truncated
+ renderTextWithNewlines truncated
when isTruncated <| Lucid.span_ [Lucid.class_ "event-truncated"] "..."
renderToolCallEvent :: (Monad m) => Text -> UTCTime -> UTCTime -> Lucid.HtmlT m ()
@@ -2498,8 +2499,8 @@ renderToolResultEvent content timestamp now =
then
Lucid.details_ [Lucid.class_ "result-collapsible"] <| do
Lucid.summary_ "Show output"
- Lucid.pre_ [Lucid.class_ "event-content tool-output"] (Lucid.toHtml content)
- else Lucid.pre_ [Lucid.class_ "event-content tool-output"] (Lucid.toHtml content)
+ Lucid.pre_ [Lucid.class_ "event-content tool-output"] (renderDecodedToolResult content)
+ else Lucid.pre_ [Lucid.class_ "event-content tool-output"] (renderDecodedToolResult content)
renderCostEvent :: (Monad m) => Text -> Lucid.HtmlT m ()
renderCostEvent content =
@@ -2540,6 +2541,26 @@ renderCollapsibleOutput content =
Lucid.pre_ [Lucid.class_ "tool-output-pre"] (Lucid.toHtml content)
else Lucid.pre_ [Lucid.class_ "tool-output-pre"] (Lucid.toHtml content)
+-- | Render text with literal \n replaced by <br> tags
+renderTextWithNewlines :: (Monad m) => Text -> Lucid.HtmlT m ()
+renderTextWithNewlines txt =
+ let parts = Text.splitOn "\\n" txt
+ in traverse_ renderPart (zip [0 ..] parts)
+ where
+ renderPart (idx, part) = do
+ Lucid.toHtml part
+ when (idx < length parts - 1) <| Lucid.br_ []
+
+-- | Decode JSON tool result and render in a user-friendly way
+renderDecodedToolResult :: (Monad m) => Text -> Lucid.HtmlT m ()
+renderDecodedToolResult content =
+ case Aeson.decode (LBS.fromStrict (str content)) of
+ Just (Aeson.Object obj) ->
+ case KeyMap.lookup "output" obj of
+ Just (Aeson.String output) -> Lucid.toHtml output
+ _ -> Lucid.toHtml content -- Fallback to raw if no output field
+ _ -> Lucid.toHtml content -- Fallback to raw if not JSON
+
agentLogScrollScript :: (Monad m) => Lucid.HtmlT m ()
agentLogScrollScript =
Lucid.script_