Skip to content

chore: upgrade flo-ai and ui improvements#279

Merged
vizsatiz merged 2 commits into
developfrom
chore/wokrflow-improvements
Apr 22, 2026
Merged

chore: upgrade flo-ai and ui improvements#279
vizsatiz merged 2 commits into
developfrom
chore/wokrflow-improvements

Conversation

@vishnurk6247
Copy link
Copy Markdown
Member

@vishnurk6247 vishnurk6247 commented Apr 22, 2026

Summary by CodeRabbit

  • New Features

    • Unified attachment display combining images and documents with a "+N" popover for additional items.
  • Bug Fixes

    • Added scroll support to YAML editor when content exceeds available space.
    • Improved message content handling for various input types.
  • Chores

    • Updated dependencies.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

This PR updates the Gemini LLM message content handler to properly extract content from dict responses, enhances ChatBot attachment display with combined rendering and popover overflow handling, enables YAML editor container scrolling, and bumps flo-ai dependencies across server modules from 1.1.3 to 1.1.4.

Changes

Cohort / File(s) Summary
Gemini LLM Response Handler
flo_ai/flo_ai/llm/gemini_llm.py
Modified get_message_content to handle str and dict inputs explicitly; extracts content from dict responses and returns empty string for non-matching types instead of implicitly returning None.
ChatBot Attachment Display
wavefront/client/src/components/ChatBot.tsx
Added unified combinedAttachments list interleaving images and documents with kind tags; renders first 2 attachments inline and displays remaining count in a Popover; updated removal handlers to use original indices and applied flex-wrap layout.
YAML Editor Scrolling
wavefront/client/src/pages/apps/[appId]/workflows/[id].tsx
Added overflow-auto to YAML editor wrapper container to enable vertical scrolling when content exceeds available space.
Dependency Version Bumps
wavefront/server/modules/agents_module/pyproject.toml, wavefront/server/modules/knowledge_base_module/pyproject.toml, wavefront/server/modules/tools_module/pyproject.toml
Updated flo-ai dependency from version 1.1.3 to 1.1.4 across three server modules.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • fix(flo-ai): for parser issue #244: Modifies get_message_content behavior in flo_ai/flo_ai/llm/gemini_llm.py with overlapping changes to dict response handling and fallback logic.

Suggested reviewers

  • vizsatiz

Poem

🐰 Attachments dance in pairs, with popover delight,
Gemini learns to read dicts with newfound sight,
YAML scrolls freely, no overflow fright,
Bumped versions ensure the dependencies align just right! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: flo-ai dependency upgrade (versions 1.1.3→1.1.4 across three modules) and UI improvements (ChatBot attachment handling, workflow editor scrolling).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/wokrflow-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
wavefront/client/src/components/ChatBot.tsx (3)

110-116: Minor: consolidate redundant slice math and consider memoization.

remainingCombinedAttachmentsCount and remainingCombinedAttachments are two computations of the same thing — prefer driving the count from remainingCombinedAttachments.length. Also, because combinedAttachments is rebuilt on every render, you may want to wrap the derivation in useMemo to avoid spreading both arrays and re-mapping on unrelated state changes (e.g. typing in the input). Not a correctness issue, purely cleanliness.

♻️ Proposed refactor
-  const combinedAttachments = [
-    ...uploadedImages.map((image, index) => ({ kind: 'image' as const, image, originalIndex: index })),
-    ...uploadedDocuments.map((document, index) => ({ kind: 'document' as const, document, originalIndex: index })),
-  ];
-  const visibleCombinedAttachments = combinedAttachments.slice(0, 2);
-  const remainingCombinedAttachmentsCount = Math.max(combinedAttachments.length - visibleCombinedAttachments.length, 0);
-  const remainingCombinedAttachments = combinedAttachments.slice(2);
+  const combinedAttachments = useMemo(
+    () => [
+      ...uploadedImages.map((image, index) => ({ kind: 'image' as const, image, originalIndex: index })),
+      ...uploadedDocuments.map((document, index) => ({ kind: 'document' as const, document, originalIndex: index })),
+    ],
+    [uploadedImages, uploadedDocuments]
+  );
+  const visibleCombinedAttachments = combinedAttachments.slice(0, 2);
+  const remainingCombinedAttachments = combinedAttachments.slice(2);
+  const remainingCombinedAttachmentsCount = remainingCombinedAttachments.length;

(Add useMemo to the react import.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wavefront/client/src/components/ChatBot.tsx` around lines 110 - 116, Derive
remainingCombinedAttachmentsCount from remainingCombinedAttachments.length and
memoize the combined attachments derivation: wrap building combinedAttachments,
visibleCombinedAttachments, remainingCombinedAttachments (currently using
combinedAttachments.slice(...)) in a useMemo hook so they’re only recalculated
when uploadedImages or uploadedDocuments change; compute
visibleCombinedAttachments = combinedAttachments.slice(0,2),
remainingCombinedAttachments = combinedAttachments.slice(2) inside the memo and
set remainingCombinedAttachmentsCount = remainingCombinedAttachments.length,
referencing the existing symbols combinedAttachments,
visibleCombinedAttachments, remainingCombinedAttachments,
remainingCombinedAttachmentsCount and importing useMemo from react.

235-281: Use a stable compound key for visible attachments.

Keying the visible chip list by the loop index is functionally fine today but becomes fragile once removals and mixed kinds are involved — React may reuse a DOM node for an image chip as a document chip (or vice-versa) during reorders, causing brief visual glitches. The popover list already uses a compound key; mirror that here for consistency.

♻️ Proposed change
-                {visibleCombinedAttachments.map((attachment, index) =>
+                {visibleCombinedAttachments.map((attachment) =>
                   attachment.kind === 'image' ? (
                     <div
-                      key={index}
+                      key={`image-${attachment.originalIndex}`}
                       ...
                     >
                       ...
                     </div>
                   ) : (
                     <div
-                      key={index}
+                      key={`document-${attachment.originalIndex}`}
                       ...
                     >
                       ...
                     </div>
                   )
                 )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wavefront/client/src/components/ChatBot.tsx` around lines 235 - 281, The
visible attachments list uses the loop index as key which can cause DOM reuse
glitches; update the key on the mapped elements in ChatBot.tsx (the mapped
visibleCombinedAttachments in the JSX that renders image/document chips) to a
stable compound key such as combining attachment.kind and
attachment.originalIndex (e.g.,
`${attachment.kind}-${attachment.originalIndex}`) to match the popover list's
approach and ensure consistent identity across removals and mixed kinds; keep
the existing onClick handlers (handleRemoveImage / handleRemoveDocument)
unchanged.

231-233: Container mixes flex-wrap with overflow-x-auto.

Switching the outer wrapper to flex-wrap means chips will wrap to new lines, so overflow-x-auto on the same element effectively becomes dead styling (no horizontal overflow can occur). Either drop overflow-x-auto or keep the previous non-wrapping layout with horizontal scroll — mixing them is confusing and can also cause unexpected vertical growth pushing the composer down when many chips are present.

🧹 Suggested cleanup
-            <div className="flex min-w-0 flex-1 flex-wrap items-center gap-2 overflow-x-auto">
+            <div className="flex min-w-0 flex-1 flex-wrap items-center gap-2">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wavefront/client/src/components/ChatBot.tsx` around lines 231 - 233, The
attachments container currently mixes "flex-wrap" with "overflow-x-auto" in the
div that renders when (uploadedImages.length > 0 || uploadedDocuments.length >
0), which prevents horizontal scrolling and causes vertical growth; decide which
behavior you want and fix the className accordingly — either remove
"overflow-x-auto" to keep wrapping chips on multiple lines, or remove
"flex-wrap" (and ensure "flex-nowrap" if needed) so the container stays
single-line and horizontal scrolling via "overflow-x-auto" works; update the
className on that div in ChatBot.tsx (the Scrollable Attachments Container
rendering the uploadedImages/uploadedDocuments chips) to reflect the chosen
option.
wavefront/client/src/pages/apps/[appId]/workflows/[id].tsx (1)

729-729: Verify that overflow behavior works as intended.

Adding overflow-auto to this div may not achieve the intended scrolling behavior because the div lacks a height constraint (e.g., flex-1 or max-h-*). Without such a constraint, the div will naturally size to fit its content (CodeMirror's 500px + text), and overflow-auto won't trigger scrolling.

Additionally, this creates nested scroll containers since the parent DialogContent already has overflow-y-auto and max-h-[90vh] (line 725), which can lead to confusing UX with multiple scrollbars.

If the goal is to make the editor area scroll independently while keeping the header and footer fixed, consider adding flex-1 or an explicit height constraint to this div. Otherwise, the DialogContent's existing overflow already handles scrolling for the entire dialog.

Please verify the rendered behavior to confirm whether this achieves the desired UX. You can test with YAML content that exceeds 500px in the editor to see which container scrolls.

🎨 Suggested layout pattern for independent content scrolling

If the intent is to keep header/footer fixed while only the editor area scrolls:

-          <div className="flex flex-col gap-3 overflow-auto py-4">
+          <div className="flex flex-1 flex-col gap-3 overflow-auto py-4">

This requires the DialogContent to use flex layout (which it likely does), allowing this div to take available space and scroll independently.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wavefront/client/src/pages/apps/`[appId]/workflows/[id].tsx at line 729, The
div with class "flex flex-col gap-3 overflow-auto py-4" inside the DialogContent
creates a nested scroll container without a height constraint so overflow-auto
may never trigger; update that div (or DialogContent layout) to give it a
height/flex constraint (e.g., add flex-1 or a max-h-* class) if you want the
editor area (CodeMirror) to scroll independently, or remove overflow-auto to
rely on DialogContent's existing overflow-y-auto/max-h-[90vh] for
single-container scrolling; after changing, verify rendered behavior in the
[appId]/workflows/[id].tsx dialog by loading YAML content that makes CodeMirror
exceed 500px to confirm which container scrolls and that header/footer remain
fixed as intended.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@flo_ai/flo_ai/llm/gemini_llm.py`:
- Around line 208-213: The get_message_content function currently returns None
for inputs that are neither str nor dict; change its signature from response:
Dict[str, Any] to response: Any and ensure it always returns a str by adding a
final fallback that returns str(response) (or '' if response is None) so callers
like agent.py (which call self.llm.get_message_content(...).strip().upper()) and
tests expecting __str__ behavior won't break; keep the existing handling for str
and dict (use response.get('content') -> str if present, else '') but ensure
every code path returns a string.

In `@wavefront/client/src/components/ChatBot.tsx`:
- Around line 293-294: In ChatBot.tsx, remove the stray "b" token from the
PopoverContent className (the JSX element using PopoverContent with className="b
flex flex-col gap-1.5 opacity-80"); update that className to only valid Tailwind
utilities (e.g., "flex flex-col gap-1.5 opacity-80") so there are no
invalid/typo classes left in the PopoverContent element.

---

Nitpick comments:
In `@wavefront/client/src/components/ChatBot.tsx`:
- Around line 110-116: Derive remainingCombinedAttachmentsCount from
remainingCombinedAttachments.length and memoize the combined attachments
derivation: wrap building combinedAttachments, visibleCombinedAttachments,
remainingCombinedAttachments (currently using combinedAttachments.slice(...)) in
a useMemo hook so they’re only recalculated when uploadedImages or
uploadedDocuments change; compute visibleCombinedAttachments =
combinedAttachments.slice(0,2), remainingCombinedAttachments =
combinedAttachments.slice(2) inside the memo and set
remainingCombinedAttachmentsCount = remainingCombinedAttachments.length,
referencing the existing symbols combinedAttachments,
visibleCombinedAttachments, remainingCombinedAttachments,
remainingCombinedAttachmentsCount and importing useMemo from react.
- Around line 235-281: The visible attachments list uses the loop index as key
which can cause DOM reuse glitches; update the key on the mapped elements in
ChatBot.tsx (the mapped visibleCombinedAttachments in the JSX that renders
image/document chips) to a stable compound key such as combining attachment.kind
and attachment.originalIndex (e.g.,
`${attachment.kind}-${attachment.originalIndex}`) to match the popover list's
approach and ensure consistent identity across removals and mixed kinds; keep
the existing onClick handlers (handleRemoveImage / handleRemoveDocument)
unchanged.
- Around line 231-233: The attachments container currently mixes "flex-wrap"
with "overflow-x-auto" in the div that renders when (uploadedImages.length > 0
|| uploadedDocuments.length > 0), which prevents horizontal scrolling and causes
vertical growth; decide which behavior you want and fix the className
accordingly — either remove "overflow-x-auto" to keep wrapping chips on multiple
lines, or remove "flex-wrap" (and ensure "flex-nowrap" if needed) so the
container stays single-line and horizontal scrolling via "overflow-x-auto"
works; update the className on that div in ChatBot.tsx (the Scrollable
Attachments Container rendering the uploadedImages/uploadedDocuments chips) to
reflect the chosen option.

In `@wavefront/client/src/pages/apps/`[appId]/workflows/[id].tsx:
- Line 729: The div with class "flex flex-col gap-3 overflow-auto py-4" inside
the DialogContent creates a nested scroll container without a height constraint
so overflow-auto may never trigger; update that div (or DialogContent layout) to
give it a height/flex constraint (e.g., add flex-1 or a max-h-* class) if you
want the editor area (CodeMirror) to scroll independently, or remove
overflow-auto to rely on DialogContent's existing overflow-y-auto/max-h-[90vh]
for single-container scrolling; after changing, verify rendered behavior in the
[appId]/workflows/[id].tsx dialog by loading YAML content that makes CodeMirror
exceed 500px to confirm which container scrolls and that header/footer remain
fixed as intended.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7a18a2a-43fe-4ed5-bd51-cdf438248ba5

📥 Commits

Reviewing files that changed from the base of the PR and between a276106 and f923f8e.

⛔ Files ignored due to path filters (1)
  • wavefront/server/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • flo_ai/flo_ai/llm/gemini_llm.py
  • wavefront/client/src/components/ChatBot.tsx
  • wavefront/client/src/pages/apps/[appId]/workflows/[id].tsx
  • wavefront/server/modules/agents_module/pyproject.toml
  • wavefront/server/modules/knowledge_base_module/pyproject.toml
  • wavefront/server/modules/tools_module/pyproject.toml

Comment on lines 208 to +213
def get_message_content(self, response: Dict[str, Any]) -> str:
if isinstance(response, str):
return response
if hasattr(response, 'content') and response.content is not None:
return str(response.content)
return ''
if isinstance(response, dict):
content = response.get('content')
return str(content) if content is not None else ''
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: missing fallback return — non-str/non-dict inputs now implicitly return None, breaking callers and existing tests.

Previously, get_message_content ended with return '' (or str(response.content) via hasattr), so non-str/non-dict inputs always produced a string. After this change, any input that is neither str nor dict falls through and returns None implicitly, which:

  1. Breaks flo_ai/flo_ai/agent/agent.py:616, which chains .strip().upper() on the result (self.llm.get_message_content(analysis_response).strip().upper()). A None return raises AttributeError: 'NoneType' object has no attribute 'strip'.
  2. Breaks the existing integration test test_get_message_content_object in flo_ai/tests/integration-tests/test_gemini_llm_real.py:252-261, which passes a MockObject and expects 'Mock object string' via __str__().
  3. Diverges from the consistent pattern in sibling LLMs (e.g., flo_ai/flo_ai/llm/anthropic_llm.py:217-221) which fall back to str(response) for non-dict inputs.

Also note the declared type response: Dict[str, Any] is narrower than the actual accepted types — consider widening to Any to match the other LLM implementations and the stringly-typed MockObject case.

🐛 Proposed fix — restore `str(response)` fallback and align signature
-    def get_message_content(self, response: Dict[str, Any]) -> str:
-        if isinstance(response, str):
-            return response
-        if isinstance(response, dict):
-            content = response.get('content')
-            return str(content) if content is not None else ''
+    def get_message_content(self, response: Any) -> str:
+        if isinstance(response, str):
+            return response
+        if isinstance(response, dict):
+            return str(response.get('content', ''))
+        return str(response)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@flo_ai/flo_ai/llm/gemini_llm.py` around lines 208 - 213, The
get_message_content function currently returns None for inputs that are neither
str nor dict; change its signature from response: Dict[str, Any] to response:
Any and ensure it always returns a str by adding a final fallback that returns
str(response) (or '' if response is None) so callers like agent.py (which call
self.llm.get_message_content(...).strip().upper()) and tests expecting __str__
behavior won't break; keep the existing handling for str and dict (use
response.get('content') -> str if present, else '') but ensure every code path
returns a string.

Comment on lines +293 to +294
<PopoverContent align="start" className="w-64 bg-gray-800 p-2">
<div className="b flex flex-col gap-1.5 opacity-80">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stray b class in popover content.

className="b flex flex-col gap-1.5 opacity-80" contains a leftover b class that isn't a valid Tailwind utility and appears to be a typo.

🧹 Proposed fix
-                    <PopoverContent align="start" className="w-64 bg-gray-800 p-2">
-                      <div className="b flex flex-col gap-1.5 opacity-80">
+                    <PopoverContent align="start" className="w-64 bg-gray-800 p-2">
+                      <div className="flex flex-col gap-1.5 opacity-80">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<PopoverContent align="start" className="w-64 bg-gray-800 p-2">
<div className="b flex flex-col gap-1.5 opacity-80">
<PopoverContent align="start" className="w-64 bg-gray-800 p-2">
<div className="flex flex-col gap-1.5 opacity-80">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wavefront/client/src/components/ChatBot.tsx` around lines 293 - 294, In
ChatBot.tsx, remove the stray "b" token from the PopoverContent className (the
JSX element using PopoverContent with className="b flex flex-col gap-1.5
opacity-80"); update that className to only valid Tailwind utilities (e.g.,
"flex flex-col gap-1.5 opacity-80") so there are no invalid/typo classes left in
the PopoverContent element.

@vizsatiz vizsatiz merged commit 904e73e into develop Apr 22, 2026
9 checks passed
@vizsatiz vizsatiz deleted the chore/wokrflow-improvements branch April 22, 2026 10:15
thomastomy5 pushed a commit that referenced this pull request Apr 27, 2026
* chore: upgrade flo-ai and ui improvements

* fix(flo-ai): fix gemini response
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants