-
Notifications
You must be signed in to change notification settings - Fork 30
chore: upgrade flo-ai and ui improvements #279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,7 @@ import { Switch } from '@app/components/ui/switch'; | |||||||||
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@app/components/ui/select'; | ||||||||||
| import { Spinner } from '@app/components/ui/spinner'; | ||||||||||
| import { Textarea } from '@app/components/ui/textarea'; | ||||||||||
| import { Popover, PopoverContent, PopoverTrigger } from '@app/components/ui/popover'; | ||||||||||
| import { LLMInferenceConfig } from '@app/types/llm-inference-config'; | ||||||||||
| import { ChatMessageContent, ImageContent, DocumentContent } from '@app/types/chat-message'; | ||||||||||
| import clsx from 'clsx'; | ||||||||||
|
|
@@ -106,6 +107,13 @@ const ChatBot = ({ | |||||||||
| const variablesModalRef = useRef<HTMLDivElement>(null); | ||||||||||
| const [showLogic, setShowLogic] = useState(true); | ||||||||||
| const [selectValue, setSelectValue] = useState<string>(''); | ||||||||||
| 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); | ||||||||||
|
|
||||||||||
| return ( | ||||||||||
| <div className="flex h-full w-full flex-col gap-7"> | ||||||||||
|
|
@@ -222,56 +230,81 @@ const ChatBot = ({ | |||||||||
|
|
||||||||||
| {/* Scrollable Attachments Container */} | ||||||||||
| {(uploadedImages.length > 0 || uploadedDocuments.length > 0) && ( | ||||||||||
| <div className="flex min-w-0 flex-1 items-center gap-2 overflow-x-auto"> | ||||||||||
| {/* Uploaded Images */} | ||||||||||
| {uploadedImages.length > 0 && ( | ||||||||||
| <div className="flex shrink-0 gap-2"> | ||||||||||
| {uploadedImages.map((image, index) => ( | ||||||||||
| <div className="flex min-w-0 flex-1 flex-wrap items-center gap-2 overflow-x-auto"> | ||||||||||
| <div className="flex shrink-0 gap-2"> | ||||||||||
| {visibleCombinedAttachments.map((attachment, index) => | ||||||||||
| attachment.kind === 'image' ? ( | ||||||||||
| <div | ||||||||||
| key={index} | ||||||||||
| className="group relative flex items-center gap-1 rounded-lg border border-gray-200 bg-white p-1 transition-colors hover:border-gray-300" | ||||||||||
| > | ||||||||||
| <img src={image.base64} alt={image.file.name} className="h-6 w-6 rounded object-cover" /> | ||||||||||
| <img | ||||||||||
| src={attachment.image.base64} | ||||||||||
| alt={attachment.image.file.name} | ||||||||||
| className="h-6 w-6 rounded object-cover" | ||||||||||
| /> | ||||||||||
| <div className="flex flex-col"> | ||||||||||
| <p className="max-w-[120px] truncate text-[8px] font-medium text-gray-800">{image.file.name}</p> | ||||||||||
| <p className="text-[8px] text-gray-500">{formatFileSize(image.file.size)}</p> | ||||||||||
| <p className="max-w-[120px] truncate text-[8px] font-medium text-gray-800"> | ||||||||||
| {attachment.image.file.name} | ||||||||||
| </p> | ||||||||||
| <p className="text-[8px] text-gray-500">{formatFileSize(attachment.image.file.size)}</p> | ||||||||||
| </div> | ||||||||||
| <button | ||||||||||
| onClick={() => handleRemoveImage(index)} | ||||||||||
| onClick={() => handleRemoveImage(attachment.originalIndex)} | ||||||||||
| className="ml-2 text-red-500 transition-colors hover:text-red-700" | ||||||||||
| title="Remove image" | ||||||||||
| > | ||||||||||
| <X /> | ||||||||||
| </button> | ||||||||||
| </div> | ||||||||||
| ))} | ||||||||||
| </div> | ||||||||||
| )} | ||||||||||
|
|
||||||||||
| {/* Uploaded Documents */} | ||||||||||
| {uploadedDocuments.length > 0 && ( | ||||||||||
| <div className="flex shrink-0 gap-2"> | ||||||||||
| {uploadedDocuments.map((doc, index) => ( | ||||||||||
| ) : ( | ||||||||||
| <div | ||||||||||
| key={index} | ||||||||||
| className="group relative flex items-center gap-2 rounded-lg border border-gray-200 bg-white p-2 transition-colors hover:border-gray-300" | ||||||||||
| > | ||||||||||
| <div className="text-gray-600">📄</div> | ||||||||||
| <div className="flex flex-col"> | ||||||||||
| <p className="max-w-[120px] truncate text-[8px] font-medium text-gray-800">{doc.file.name}</p> | ||||||||||
| <p className="text-[8px] text-gray-500">{formatFileSize(doc.file.size)}</p> | ||||||||||
| <p className="max-w-[120px] truncate text-[8px] font-medium text-gray-800"> | ||||||||||
| {attachment.document.file.name} | ||||||||||
| </p> | ||||||||||
| <p className="text-[8px] text-gray-500">{formatFileSize(attachment.document.file.size)}</p> | ||||||||||
| </div> | ||||||||||
| <button | ||||||||||
| onClick={() => handleRemoveDocument(index)} | ||||||||||
| onClick={() => handleRemoveDocument(attachment.originalIndex)} | ||||||||||
| className="ml-2 text-red-500 transition-colors hover:text-red-700" | ||||||||||
| title="Remove document" | ||||||||||
| > | ||||||||||
| <X /> | ||||||||||
| </button> | ||||||||||
| </div> | ||||||||||
| ))} | ||||||||||
| </div> | ||||||||||
| )} | ||||||||||
| ) | ||||||||||
| )} | ||||||||||
| {remainingCombinedAttachmentsCount > 0 && ( | ||||||||||
| <Popover> | ||||||||||
| <PopoverTrigger asChild> | ||||||||||
| <button | ||||||||||
| type="button" | ||||||||||
| className="flex cursor-pointer items-center rounded-lg border border-gray-200 bg-white px-2 text-xs font-medium text-gray-600 transition-colors hover:border-gray-300" | ||||||||||
| title="Show remaining attachments" | ||||||||||
| > | ||||||||||
| +{remainingCombinedAttachmentsCount} | ||||||||||
| </button> | ||||||||||
| </PopoverTrigger> | ||||||||||
| <PopoverContent align="start" className="w-64 bg-gray-800 p-2"> | ||||||||||
| <div className="b flex flex-col gap-1.5 opacity-80"> | ||||||||||
|
Comment on lines
+293
to
+294
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stray
🧹 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| {remainingCombinedAttachments.map((attachment, index) => ( | ||||||||||
| <p | ||||||||||
| key={`${attachment.kind}-${attachment.originalIndex}-${index}`} | ||||||||||
| className="truncate text-xs text-white" | ||||||||||
| > | ||||||||||
| {attachment.kind === 'image' ? attachment.image.file.name : attachment.document.file.name} | ||||||||||
| </p> | ||||||||||
| ))} | ||||||||||
| </div> | ||||||||||
| </PopoverContent> | ||||||||||
| </Popover> | ||||||||||
| )} | ||||||||||
| </div> | ||||||||||
| </div> | ||||||||||
| )} | ||||||||||
| </div> | ||||||||||
|
|
||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: missing fallback return — non-str/non-dict inputs now implicitly return
None, breaking callers and existing tests.Previously,
get_message_contentended withreturn ''(orstr(response.content)viahasattr), so non-str/non-dictinputs always produced a string. After this change, any input that is neitherstrnordictfalls through and returnsNoneimplicitly, which:flo_ai/flo_ai/agent/agent.py:616, which chains.strip().upper()on the result (self.llm.get_message_content(analysis_response).strip().upper()). ANonereturn raisesAttributeError: 'NoneType' object has no attribute 'strip'.test_get_message_content_objectinflo_ai/tests/integration-tests/test_gemini_llm_real.py:252-261, which passes aMockObjectand expects'Mock object string'via__str__().flo_ai/flo_ai/llm/anthropic_llm.py:217-221) which fall back tostr(response)for non-dict inputs.Also note the declared type
response: Dict[str, Any]is narrower than the actual accepted types — consider widening toAnyto match the other LLM implementations and the stringly-typedMockObjectcase.🐛 Proposed fix — restore `str(response)` fallback and align signature
🤖 Prompt for AI Agents