-
Notifications
You must be signed in to change notification settings - Fork 47
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
feat: Orchestrator manager in client-example #72
base: main
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis pull request introduces a comprehensive update to the example UI client, focusing on state management, routing, and user interface improvements. The changes span multiple files and include the addition of Zustand for global state management, new routing configurations, enhanced chat and orchestrator functionality, and significant styling updates. The modifications aim to create a more dynamic and interactive user experience with improved navigation, chat history management, and theme support. Changes
Sequence DiagramsequenceDiagram
participant User
participant UI
participant AppStore
participant WebSocket
participant Server
User->>UI: Select Chat
UI->>AppStore: Update Current Orchestrator
UI->>UI: Navigate to Chat Route
UI->>WebSocket: Fetch Chat History
WebSocket->>Server: Request Chat Data
Server-->>WebSocket: Return Chat History
WebSocket-->>UI: Populate Chat Messages
UI->>AppStore: Store Messages
Possibly related PRs
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🔇 Additional comments (1)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 24
🔭 Outside diff range comments (1)
clients/example-ui/src/hooks/use-single-chat-history.tsx (1)
Line range hint
67-85
: Fix hardcoded URL and inconsistent error handling in refreshHistory.The refreshHistory function uses a hardcoded URL and has different error handling compared to fetchHistory.
const refreshHistory = async () => { setLoading(true); + setError(null); try { const response = await fetch( - `http://localhost:8081/api/history/${userId}/${chatId}` + `${import.meta.env.VITE_API_URL}/api/history/${userId}?orchestratorId=${currentOrchestratorId}&chatId=${currentChatId}` ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); + console.log('📥 Refreshed history:', data); setHistory(data); - setError(null); } catch (err) { + console.error('❌ Error refreshing history:', err); setError( err instanceof Error ? err.message : "Failed to fetch chat history" ); - console.error("Error fetching chat history:", err); } finally { setLoading(false); } };
🧹 Nitpick comments (29)
clients/example-ui/src/hooks/use-chat-histories.tsx (2)
28-69
: Robust fetch logic.
The error handling andconsole.error
usage look sound. Consider also verifying ifdata
is an array before calling.map()
, to guard against unexpected API responses.if (!Array.isArray(data)) { - const formattedItems = data.map((item: any) => ({ + throw new Error('Received non-array response for orchestrators'); }
80-83
:refreshHistory
might not re-fetch.
TherefreshHistory
function resets loading & error states but does not callfetchChatHistories
again. If you want the button or action to truly refresh data, invokefetchChatHistories
withinrefreshHistory
.const refreshHistory = () => { setLoading(true); setError(null); + fetchChatHistories(); // call the original fetch function };
clients/example-ui/src/components/message-list.tsx (1)
21-75
: Consider reducing repetitive styling.
The switch statement repeats many styling rules across cases. Consolidate common styles for easier maintenance.switch (msg.type) { case "input": // ... break; case "output": // ... break; // ... } - let baseStyles = ` + const baseStyles = ` px-4 py-2 rounded-lg max-w-[80%] font-medium relative overflow-hidden bg-card text-foreground // ... `; // Then assemble bubbleClass by combining baseStyles + type-derived stylesclients/example-ui/src/components/chat-ui.tsx (1)
95-101
: Progress feedback.
Good use of a spinner to handle theloading
state. Keep watch on potential naming conflicts withisLoading
vs.loading
from the hook.examples/example-server.ts (7)
34-37
: Optional parameters enhance flexibility.Allowing an optional
params
object to configuremodel
andtemperature
is a smart way to makecreateDaydreamsAgent
more extensible. You might want to validate the temperature range (e.g., [0.0 – 1.0]) and confirm that the selected model exists or is supported.
126-129
: Consistent language usage.The “Envoyer uniquement...” comment is in French, while the rest of the code is predominantly in English. For maintainability, consider aligning all comments to the same language.
141-141
: Clear error message.It’s good to throw an error for invalid input; this helps catch malformed requests early. Consider adding a user-friendly error code or reason in the message to assist debugging.
Line range hint
179-190
: Potential for logging or persistent storage of outputs.You dispatch the user’s message and send real-time replies. If you ever need detailed logs or post-process the chat responses, consider storing or tagging the
chat_reply
outputs in a permanent data layer.
225-232
: Environment-based CORS configuration.Deterministic CORS origins are fine in a local environment but may need to be loaded from environment variables or a config file in production setups to avoid accidental exposure.
403-451
: Validate model and temperature on orchestrator creation.You verify
userId
presence but do not check iftemperature
ormodel
are valid. Consider enforcing numeric bounds or an enum of permissible models to avoid unexpected runtime issues.
509-569
: Potentially unify logic withcreateDaydreamsAgent
.
createOrchestratorFromDb
largely duplicates the initialization steps increateDaydreamsAgent
. Consider refactoring to share common initialization (LLM, vector DB, handlers, etc.) to reduce duplication and streamline future changes.clients/example-ui/src/routes/my-agents/index.tsx (1)
1-6
: Clear separation of concerns.Creating a
/my-agents/
route and mapping it toMyAgentsPage
maintains good structure. Add tests or checks to ensure the page correctly handles missing or invalid agent data.clients/example-ui/src/routes/index.lazy.tsx (1)
2-2
: Fix import statement formatting.Remove extra space after
import
keyword to maintain consistent formatting.-import ChatUI from "@/components/chat-list"; +import ChatUI from "@/components/chat-list";clients/example-ui/src/components/debug-panel.tsx (1)
4-10
: Improve type safety of MESSAGE_TYPES.Consider using an enum or union type for better type safety and autocompletion.
-const MESSAGE_TYPES = { - STATE: 'state', - WELCOME: 'welcome', - USER: 'user', - DEBUG: 'debug', - RESPONSE: 'response' -} as const; +enum MessageType { + STATE = 'state', + WELCOME = 'welcome', + USER = 'user', + DEBUG = 'debug', + RESPONSE = 'response' +}clients/example-ui/src/routes/my-agents/chats/$chatId.tsx (1)
9-14
: Add type safety for route parameters.Consider adding type safety for the route parameters to catch potential issues at compile time.
- validateParams: (params): ChatParams => { + validateParams: (params: { chatId: string; orchestratorId: string }): ChatParams => {examples/types.ts (1)
14-20
: Consider using a more specific type forinitialMessage
.Using
any
type forinitialMessage
reduces type safety. Consider using a more specific type that matches the expected message structure.export interface CreateOrchestratorParams { name?: string; userId: string; model?: string; temperature?: number; - initialMessage?: any; + initialMessage?: ChatMessage; // Define or import ChatMessage type }clients/example-ui/src/hooks/use-daydreams.tsx (2)
31-31
: Fix typo in console log message.There's a typo in the console log message: "REceived" should be "Received".
- console.log("REceived message from server", event.data); + console.log("Received message from server", event.data);
52-60
: Consider caching the generated user ID.The current implementation generates a new user ID as fallback on every
sendGoal
call. Consider using the cacheduserIdRef.current
instead.const sendGoal = useCallback((goal: string, orchestratorId?: string, userId?: string) => { if (wsRef.current?.readyState === WebSocket.OPEN) { wsRef.current.send( JSON.stringify({ goal, - userId: userId || generateUserId(), + userId: userId || userIdRef.current, orchestratorId, }) ); } - }, []); + }, [userIdRef]);clients/example-ui/src/hooks/use-chat-history.tsx (3)
63-63
: Remove unuseduserId
from dependency array.The
userId
is included in theuseCallback
dependencies but is not used within the callback function.- }, [chatId, currentOrchestratorId, userId]); + }, [chatId, currentOrchestratorId]);
43-56
: Enhance error handling and add type safety for API response.The current implementation could benefit from improved error handling and type safety:
- Add timeout for fetch request
- Add type assertion for API response
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8081'; const url = `${apiUrl}/api/orchestrators/${currentOrchestratorId}`; - const response = await fetch(url); + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); + + const response = await fetch(url, { signal: controller.signal }); + clearTimeout(timeoutId); if (!response.ok) { const errorText = await response.text(); console.error('❌ Server error:', errorText); throw new Error(`HTTP error! status: ${response.status}, details: ${errorText}`); } - const data = await response.json(); + const data = (await response.json()) as ChatHistory; setHistory(data);
33-36
: Consider using loading state for ID availability.The current implementation sets
loading
to false when IDs are not available. Consider keeping it true to indicate that the hook is waiting for required data.if (!chatId || !currentOrchestratorId) { console.log('⏳ Waiting for IDs...', { chatId, currentOrchestratorId }); - setLoading(false); return; }
clients/example-ui/src/components/my-agents-page.tsx (2)
57-63
: Improve input accessibility.The input field lacks proper accessibility attributes.
<input type="text" value={newOrchestratorName} onChange={handleInputChange} placeholder="Orchestrator name..." className="flex-1 px-4 py-2 rounded-lg border" + aria-label="New orchestrator name" + minLength={3} + required />
36-50
: Add keyboard navigation for orchestrator selection.The orchestrator selection currently only works with mouse clicks. Consider adding keyboard navigation for better accessibility.
<div key={orch.id} onClick={() => handleSelectOrchestrator(orch.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + handleSelectOrchestrator(orch.id); + } + }} + role="button" + tabIndex={0} className={`p-4 rounded-lg border cursor-pointer transition-colors ${currentOrchestratorId === orch.id ? 'bg-primary/10 border-primary' : 'bg-card hover:border-primary'}`} >clients/example-ui/src/components/chat-list.tsx (2)
9-11
: Memoize filtered chats for better performance.The chat filtering happens on every render. Consider using useMemo to optimize performance.
-const filteredChats = chatItems.filter( - chat => chat.orchestratorId === currentOrchestratorId -); +const filteredChats = useMemo( + () => chatItems.filter(chat => chat.orchestratorId === currentOrchestratorId), + [chatItems, currentOrchestratorId] +);
13-19
: Add loading state for chat selection.The chat selection should show a loading state while updating the orchestrator.
+const [isSelecting, setIsSelecting] = useState(false); const handleChatSelect = (chatId: string) => { const chat = filteredChats.find(c => c._id === chatId); if (chat) { + setIsSelecting(true); setCurrentOrchestratorId(chat.orchestratorId); console.log('🎯 Selecting chat:', chatId, 'with orchestrator:', chat.orchestratorId); + setIsSelecting(false); } };clients/example-ui/src/hooks/use-orchestrators.tsx (1)
19-50
: Consider enhancing error handling and removing console logs.While the implementation is solid, consider these improvements:
- Add specific error handling for different HTTP status codes
- Remove or conditionally enable console logs for production
- Consider adding a retry mechanism for failed requests
const fetchOrchestrators = useCallback(async () => { - console.log('🔄 Fetching orchestrators...'); try { setLoading(true); const userId = generateUserId(); const response = await fetch( `${API_URL}/api/orchestrators?userId=${userId}` ); if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + const errorMessage = response.status === 404 ? 'No orchestrators found' : + response.status === 401 ? 'Unauthorized access' : + response.status === 503 ? 'Service unavailable' : + `HTTP error! status: ${response.status}`; + throw new Error(errorMessage); } const data = await response.json(); - console.log('📥 Received orchestrators:', data); setOrchestrators(data);clients/example-ui/src/store/use-app-store.ts (1)
5-53
: Improve type safety in interfaces.Consider these type safety improvements:
- Replace
any
with a specific type for Message.data- Use const assertion for theme values
+type MessageData = { + response?: string; + error?: Error; + metadata?: Record<string, unknown>; +}; + export interface Message { type: string; message?: string; error?: string; orchestratorId?: string; timestamp?: number; messageType?: string; - data?: any; + data?: MessageData; orchestrators?: Array<{ id: string; name: string; }>; } +const THEMES = ['light', 'dark'] as const; +type Theme = typeof THEMES[number]; + export interface AppState { // Theme - theme: 'light' | 'dark'; + theme: Theme;clients/example-ui/src/index.css (2)
Line range hint
65-120
: Consider softening text colors in dark theme for better readabilityUsing pure black text (
0 0% 0%
) on bright neon backgrounds can be harsh on users' eyes, especially in dark mode. Consider using a softer black or dark gray.Apply this change to message text colors in dark theme:
- --message-user-text: 0 0% 0%; /* Noir pur */ + --message-user-text: 0 0% 10%; /* Noir doux */ - --message-assistant-text: 0 0% 0%; /* Noir pur */ + --message-assistant-text: 0 0% 10%; /* Noir doux */ - --message-system-text: 0 0% 0%; /* Noir pur */ + --message-system-text: 0 0% 10%; /* Noir doux */ - --message-error-text: 0 0% 0%; /* Noir pur */ + --message-error-text: 0 0% 10%; /* Noir doux */ - --message-welcome-text: 0 0% 0%; /* Noir pur */ + --message-welcome-text: 0 0% 10%; /* Noir doux */ - --message-info-text: 0 0% 0%; /* Noir pur */ + --message-info-text: 0 0% 10%; /* Noir doux */
226-267
: Extract common message styles to reduce duplicationAll message components share similar gradient, shadow, and border styles. Consider extracting these into a base message class.
Apply this refactor to improve maintainability:
+ .message-base { + @apply bg-gradient-to-r shadow-lg font-medium + border border-opacity-30; + } + .message-user { - @apply bg-gradient-to-r from-[hsl(var(--message-user-from))] to-[hsl(var(--message-user-to))] + @apply message-base + from-[hsl(var(--message-user-from))] to-[hsl(var(--message-user-to))] text-[hsl(var(--message-user-text))] mr-2 self-end hover:brightness-110 shadow-lg shadow-[hsl(var(--message-user-from))]/20 - border border-[hsl(var(--message-user-from))]/30 font-medium; + border-[hsl(var(--message-user-from))]; } .message-assistant { - @apply bg-gradient-to-r from-[hsl(var(--message-assistant-from))] to-[hsl(var(--message-assistant-to))] + @apply message-base + from-[hsl(var(--message-assistant-from))] to-[hsl(var(--message-assistant-to))] text-[hsl(var(--message-assistant-text))] ml-2 shadow-lg shadow-[hsl(var(--message-assistant-from))]/20 - border border-[hsl(var(--message-assistant-from))]/30 font-medium; + border-[hsl(var(--message-assistant-from))]; } /* Apply similar changes to other message classes */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (27)
clients/example-ui/package.json
(1 hunks)clients/example-ui/src/components/app-sidebar.tsx
(4 hunks)clients/example-ui/src/components/chat-list.tsx
(1 hunks)clients/example-ui/src/components/chat-ui.tsx
(2 hunks)clients/example-ui/src/components/debug-panel.tsx
(1 hunks)clients/example-ui/src/components/message-list.tsx
(1 hunks)clients/example-ui/src/components/my-agents-page.tsx
(1 hunks)clients/example-ui/src/hooks/use-chat-histories.tsx
(1 hunks)clients/example-ui/src/hooks/use-chat-history.tsx
(3 hunks)clients/example-ui/src/hooks/use-daydreams.tsx
(4 hunks)clients/example-ui/src/hooks/use-orchestrators.tsx
(1 hunks)clients/example-ui/src/hooks/use-single-chat-history.tsx
(1 hunks)clients/example-ui/src/index.css
(3 hunks)clients/example-ui/src/routeTree.gen.ts
(7 hunks)clients/example-ui/src/routes/__root.tsx
(3 hunks)clients/example-ui/src/routes/chats/$chatId.tsx
(1 hunks)clients/example-ui/src/routes/chats/index.tsx
(1 hunks)clients/example-ui/src/routes/index.lazy.tsx
(1 hunks)clients/example-ui/src/routes/my-agents/chats/$chatId.tsx
(1 hunks)clients/example-ui/src/routes/my-agents/chats/index.tsx
(1 hunks)clients/example-ui/src/routes/my-agents/index.tsx
(1 hunks)clients/example-ui/src/store/use-app-store.ts
(1 hunks)clients/example-ui/tsconfig.json
(1 hunks)examples/example-server.ts
(8 hunks)examples/types.ts
(1 hunks)packages/core/src/core/mongo-db.ts
(4 hunks)packages/core/src/core/orchestrator.ts
(1 hunks)
🧰 Additional context used
🪛 GitHub Actions: CI
packages/core/src/core/orchestrator.ts
[error] 308-308: Type mismatch: Argument of type 'ObjectId' is not assignable to parameter of type 'string'.
🔇 Additional comments (30)
clients/example-ui/src/hooks/use-chat-histories.tsx (5)
1-2
: Imports look good.
These newly added imports (useCallback
,useAppStore
) appear to be used properly in subsequent functionality.
3-3
: Validate thegenerateUserId
usage.
Ensure that the updated logic for generating user IDs matches the rest of the application's expectations (e.g., if uniqueness or a stable user token is needed).
10-10
: Interface extension is consistent.
AddingorchestratorId
helps track the orchestrator for each chat item, aligning well with the store references.
15-16
: Great approach to asynchronous state.
Having bothloading
anderror
states provides clarity for UI feedback during fetch operations.
20-25
: Auto-selecting the first orchestrator.
The callback correctly sets an orchestrator if none is selected. Verify if this behavior is correct for multi-orchestrator environments, or if you later plan to let the user choose from a list.clients/example-ui/src/components/message-list.tsx (1)
94-97
: Conditionally displaying message type.
Good approach. Hiding the type for input/output makes sense if you want them to appear more personalized.clients/example-ui/src/components/chat-ui.tsx (5)
25-27
: Naming alignment with the existing code.
Renamingmessage
toinput
is sensible. Props in sibling components should remain consistent if referencing the same data.
33-35
: Use of route paramchatId
.
chatId
is only used for logging purposes. Double-check that you truly intend to rely solely oncurrentOrchestratorId
for chat matching instead ofchatId
.
52-55
: Scrolling to latest messages.
Scrolling logic is correct; trigger it onceallMessages
updates.
104-110
: Debug panel integration.
Provides a convenient mechanism for troubleshooting in development. Be sure to hide or strip these logs in production builds.
112-134
: Conditional form disable.
Disabling the submit button whencurrentOrchestratorId
is missing helps prevent invalid calls. Great user experience detail.examples/example-server.ts (6)
7-7
: Good type introduction.Importing the
OrchestratorData
interface from"./types"
is a neat way to keep the data structure consistent across the codebase.
42-43
: Default model and temperature.These default values are reasonable. However, ensuring the default model is available in the runtime environment will help avoid runtime errors if the model name changes or becomes unsupported.
243-291
: Graceful default creation logic.Automatically creating a default orchestrator if none is found ensures the user always has an available orchestrator. Just confirm the concurrency implications if multiple requests arrive simultaneously for the same user.
Line range hint
300-330
: Chat ID parsing and error handling.Your handling of malformed
chatId
is clear and returns an HTTP 400. This is a solid approach to prevent further processing of invalid inputs.
338-401
: Default orchestrator creation fallback.Creating a fallback orchestrator upon an empty list is a good user experience flow. Validate that this does not inadvertently overwrite or reassign orchestrators if multiple requests for the same user happen in parallel.
453-498
: Proper user matching in GET /:orchestratorId.This route checks for the
userId
in the query when finding the orchestrator, which is a good security measure. Kindly confirm the client always sends theuserId
parameter or restrict orchestrators to a single user.clients/example-ui/src/routes/my-agents/chats/index.tsx (1)
1-6
: Route structure looks solid.Defining a dedicated
/my-agents/chats/
route withChatList
simplifies the code and improves readability. Ensure you handle edge cases such as an agent without any chat IDs.clients/example-ui/src/routes/chats/index.tsx (1)
8-11
: 🛠️ Refactor suggestionRemove unused RouteComponent function.
The
RouteComponent
function is defined but never used since the route directly usesChatUI
as its component.-function RouteComponent() { - const { chatId } = Route.useParams(); - return <ChatUI chatId={chatId} /> -}Likely invalid or redundant comment.
clients/example-ui/src/components/debug-panel.tsx (1)
18-18
: Implement the DebugPanel component.The component contains a placeholder comment instead of actual implementation. Please implement the component or provide more details about the implementation plan.
Would you like me to help with implementing the DebugPanel component? I can provide a basic implementation that displays messages and state in a debug-friendly format.
clients/example-ui/src/routes/__root.tsx (2)
17-18
: LGTM! Theme configuration looks good.The ThemeProvider is properly configured with a default theme and storage key.
Line range hint
19-46
: LGTM! Layout structure is well-organized.The layout implementation using flexbox ensures proper alignment and spacing between the sidebar, main content, and right sidebar. The backdrop blur effect adds a nice visual touch while maintaining readability.
clients/example-ui/src/hooks/use-orchestrators.tsx (1)
1-18
: Well-structured hook interface!The interface design is clean, includes proper error handling, and follows React patterns. The environment-based API URL with fallback is a good practice.
clients/example-ui/src/components/app-sidebar.tsx (1)
7-8
: LGTM! Navigation setup is properly configured.The navigation updates correctly integrate with the new routing system.
Also applies to: 40-40
clients/example-ui/src/routeTree.gen.ts (1)
Line range hint
1-226
: No review needed for generated file.This file is automatically generated by TanStack Router and should not be modified manually.
packages/core/src/core/mongo-db.ts (1)
30-31
: LGTM! Interface extension looks good.The addition of
model
andtemperature
properties toOrchestratorChat
interface aligns with the AI model configuration requirements.clients/example-ui/tsconfig.json (1)
11-13
: LGTM! TypeScript configuration is properly set up.The addition of React JSX support and type definitions is correct for a React application.
clients/example-ui/package.json (1)
31-32
: LGTM! Dependencies are properly configured.The addition of Zustand for state management is a good choice, and the version is up to date.
clients/example-ui/src/index.css (2)
Line range hint
1-274
: Overall implementation looks great! 👍The theming implementation is well-structured and provides a modern, cyberpunk-inspired design that aligns well with the UI enhancements in this PR. The message styling will provide clear visual distinction between different types of messages in the chat interface.
6-62
: Verify color contrast ratios for accessibility complianceThe light theme uses bright neon colors. Please ensure all text colors meet WCAG 2.1 Level AA contrast requirements, especially for:
- Primary text on background (--foreground on --background)
- Message text colors on their respective gradient backgrounds
Run this script to analyze color contrast ratios:
#!/usr/bin/env python3 import colorsys import math def hsl_to_rgb(h, s, l): # Convert percentages to decimals s = s/100 l = l/100 return colorsys.hls_to_rgb(h/360, l, s) def luminance(r, g, b): # Convert to sRGB rgb = [x/255 if x <= 0.03928 else ((x/255 + 0.055)/1.055)**2.4 for x in [r, g, b]] return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2] def contrast_ratio(l1, l2): return (l1 + 0.05)/(l2 + 0.05) if l1 > l2 else (l2 + 0.05)/(l1 + 0.05) # Example usage for foreground on background bg_h, bg_s, bg_l = 220, 33, 98 # --background fg_h, fg_s, fg_l = 225, 47, 15 # --foreground bg_r, bg_g, bg_b = [x * 255 for x in hsl_to_rgb(bg_h, bg_s, bg_l)] fg_r, fg_g, fg_b = [x * 255 for x in hsl_to_rgb(fg_h, fg_s, fg_l)] bg_luminance = luminance(bg_r, bg_g, bg_b) fg_luminance = luminance(fg_r, fg_g, fg_b) ratio = contrast_ratio(bg_luminance, fg_luminance) print(f"Contrast ratio: {ratio:.2f}") print(f"Meets AA for large text: {ratio >= 3}") print(f"Meets AA for normal text: {ratio >= 4.5}")✅ Verification successful
Color contrast ratios meet accessibility standards
All color combinations in the light theme pass WCAG 2.1 Level AA requirements, with contrast ratios ranging from 4.57:1 to 21.00:1, exceeding the minimum requirements for both normal text (4.5:1) and large text (3:1).
🏁 Scripts executed
The following scripts were executed for the analysis:
Error: No shell scripts found
Script:
#!/bin/bash # Create Python script for contrast analysis cat << 'EOF' > analyze_contrast.py import re import colorsys import math def parse_hsl(css_var): # Extract HSL values using regex match = re.match(r'(\d+)\s+(\d+)%\s+(\d+)%', css_var) if match: return [float(x) for x in match.groups()] return None def hsl_to_rgb(h, s, l): s = s/100 l = l/100 r, g, b = colorsys.hls_to_rgb(h/360, l, s) return [x * 255 for x in (r, g, b)] def luminance(r, g, b): rgb = [x/255 if x <= 0.03928 else ((x/255 + 0.055)/1.055)**2.4 for x in [r, g, b]] return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2] def contrast_ratio(l1, l2): return (max(l1, l2) + 0.05)/(min(l1, l2) + 0.05) def check_contrast(color1_name, color1_hsl, color2_name, color2_hsl): rgb1 = hsl_to_rgb(*color1_hsl) rgb2 = hsl_to_rgb(*color2_hsl) lum1 = luminance(*rgb1) lum2 = luminance(*rgb2) ratio = contrast_ratio(lum1, lum2) return { 'pair': f"{color1_name} on {color2_name}", 'ratio': ratio, 'passes_aa_normal': ratio >= 4.5, 'passes_aa_large': ratio >= 3 } with open('clients/example-ui/src/index.css', 'r') as f: css = f.read() # Extract color variables var_pattern = r'--([^:]+):\s*([^;]+);' vars = dict(re.findall(var_pattern, css)) # Color combinations to check combinations = [ ('foreground', 'background'), ('message-user-text', 'message-user-from'), ('message-assistant-text', 'message-assistant-from'), ('message-system-text', 'message-system-from'), ('message-error-text', 'message-error-from'), ('message-welcome-text', 'message-welcome-from'), ('message-info-text', 'message-info-from') ] print("Color Contrast Analysis Report") print("=============================") for fg_name, bg_name in combinations: fg_val = vars.get(fg_name) bg_val = vars.get(bg_name) if fg_val and bg_val: fg_hsl = parse_hsl(fg_val) bg_hsl = parse_hsl(bg_val) if fg_hsl and bg_hsl: result = check_contrast(fg_name, fg_hsl, bg_name, bg_hsl) print(f"\n{result['pair']}") print(f"Contrast ratio: {result['ratio']:.2f}:1") print(f"Passes AA for normal text (4.5:1): {result['passes_aa_normal']}") print(f"Passes AA for large text (3:1): {result['passes_aa_large']}") EOF # Run the analysis python3 analyze_contrast.pyLength of output: 1087
type: "input" | "output" | "system" | "ERROR" | "OTHER" | "WELCOME" | "INFO"; | ||
message?: string; | ||
error?: string; | ||
timestamp?: number; |
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.
Potential type mismatch with other components.
The message types here differ from those referenced in chat-ui.tsx
(e.g., "user" | "assistant" | "system" | "error"
). Confirm that all references have been updated consistently.
if (currentChat?.messages) { | ||
console.log('📝 Processing messages for chat:', chatId); | ||
const formattedMessages: MessageType[] = currentChat.messages.map(msg => ({ | ||
type: msg.role, | ||
message: msg.data.content || msg.data.message || "", | ||
})); | ||
setAllMessages(formattedMessages); | ||
} | ||
}, [history]); | ||
}, [currentChat, chatId]); |
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.
Potential user ID mismatch.
currentChat?.userId
is referenced, but userId
isn’t populated in the mapped chatItems
from useChatHistories
. This could cause undefined behavior when sending goals.
let currentOrchestrator; | ||
if (orchestratorId) { | ||
const orchestratorData = await scheduledTaskDb.getOrchestratorById(orchestratorId); | ||
|
||
if (!orchestratorData) { | ||
throw new Error(`Orchestrator not found with ID: ${orchestratorId}`); | ||
} | ||
|
||
currentOrchestrator = await createOrchestratorFromDb(orchestratorData as OrchestratorData); | ||
} else { | ||
const existingOrchestrators = await scheduledTaskDb.getOrchestratorsByUserId(userId); | ||
|
||
if (existingOrchestrators.length > 0) { | ||
currentOrchestrator = await createOrchestratorFromDb(existingOrchestrators[0]); | ||
} else { | ||
const defaultOrchestratorData = { | ||
_id: new ObjectId(), | ||
name: "Default Orchestrator", | ||
userId: userId.toString(), | ||
model: "anthropic/claude-3-5-sonnet-latest", | ||
temperature: 0.3, | ||
messages: [], | ||
createdAt: new Date(), | ||
updatedAt: new Date() | ||
}; | ||
|
||
await scheduledTaskDb.createOrchestrator(defaultOrchestratorData); | ||
currentOrchestrator = await createOrchestratorFromDb(defaultOrchestratorData); | ||
} | ||
} |
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.
User ownership check recommended.
When retrieving or creating orchestrators based on orchestratorId
and userId
, ensure you verify that the orchestrator indeed belongs to the requesting user. Otherwise, a malicious user could guess another orchestrator’s ID and access it. Add a user-based ownership check to prevent data leakage.
if (orchestratorId) {
const orchestratorData = await scheduledTaskDb.getOrchestratorById(orchestratorId);
+ if (orchestratorData && orchestratorData.userId !== userId) {
+ throw new Error("Unauthorized: Orchestrator does not belong to this user");
+ }
...
}
📝 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.
let currentOrchestrator; | |
if (orchestratorId) { | |
const orchestratorData = await scheduledTaskDb.getOrchestratorById(orchestratorId); | |
if (!orchestratorData) { | |
throw new Error(`Orchestrator not found with ID: ${orchestratorId}`); | |
} | |
currentOrchestrator = await createOrchestratorFromDb(orchestratorData as OrchestratorData); | |
} else { | |
const existingOrchestrators = await scheduledTaskDb.getOrchestratorsByUserId(userId); | |
if (existingOrchestrators.length > 0) { | |
currentOrchestrator = await createOrchestratorFromDb(existingOrchestrators[0]); | |
} else { | |
const defaultOrchestratorData = { | |
_id: new ObjectId(), | |
name: "Default Orchestrator", | |
userId: userId.toString(), | |
model: "anthropic/claude-3-5-sonnet-latest", | |
temperature: 0.3, | |
messages: [], | |
createdAt: new Date(), | |
updatedAt: new Date() | |
}; | |
await scheduledTaskDb.createOrchestrator(defaultOrchestratorData); | |
currentOrchestrator = await createOrchestratorFromDb(defaultOrchestratorData); | |
} | |
} | |
let currentOrchestrator; | |
if (orchestratorId) { | |
const orchestratorData = await scheduledTaskDb.getOrchestratorById(orchestratorId); | |
if (!orchestratorData) { | |
throw new Error(`Orchestrator not found with ID: ${orchestratorId}`); | |
} | |
if (orchestratorData && orchestratorData.userId !== userId) { | |
throw new Error("Unauthorized: Orchestrator does not belong to this user"); | |
} | |
currentOrchestrator = await createOrchestratorFromDb(orchestratorData as OrchestratorData); | |
} else { | |
const existingOrchestrators = await scheduledTaskDb.getOrchestratorsByUserId(userId); | |
if (existingOrchestrators.length > 0) { | |
currentOrchestrator = await createOrchestratorFromDb(existingOrchestrators[0]); | |
} else { | |
const defaultOrchestratorData = { | |
_id: new ObjectId(), | |
name: "Default Orchestrator", | |
userId: userId.toString(), | |
model: "anthropic/claude-3-5-sonnet-latest", | |
temperature: 0.3, | |
messages: [], | |
createdAt: new Date(), | |
updatedAt: new Date() | |
}; | |
await scheduledTaskDb.createOrchestrator(defaultOrchestratorData); | |
currentOrchestrator = await createOrchestratorFromDb(defaultOrchestratorData); | |
} | |
} |
@@ -1,9 +1,9 @@ | |||
import { createLazyFileRoute } from "@tanstack/react-router"; | |||
|
|||
import ChatUI from "@/components/chat-list"; |
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.
💡 Codebase verification
Incorrect import path needs to be updated
The ChatUI component should be imported from @/components/chat-ui
instead of @/components/chat-list
. This is evidenced by:
- The component is defined in
chat-ui.tsx
- Other routes in the codebase correctly import it from this location
🔗 Analysis chain
Verify component import path.
The component is imported from chat-list
but appears to be used as a chat UI component. This seems misaligned and could indicate either:
- Wrong import path
- Incorrect component naming
- Misplaced component file
Please verify the correct import path and component organization.
Also applies to: 8-8
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for ChatUI component definitions
echo "Searching for ChatUI component definitions..."
rg -l "export.*ChatUI" src/
Length of output: 175
Script:
#!/bin/bash
# Find chat-list related files
echo "Searching for chat-list files..."
fd -t f "chat-list"
# Search for ChatUI exports in all files
echo -e "\nSearching for ChatUI exports..."
rg "export.*ChatUI"
# Look for the actual component implementation
echo -e "\nSearching for ChatUI component definition..."
ast-grep --pattern 'const ChatUI = $_'
Length of output: 757
@@ -1,9 +1,11 @@ | |||
import { createFileRoute } from '@tanstack/react-router' | |||
import ChatUI from '@/components/chat-ui' |
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.
💡 Codebase verification
Incorrect component import in routes/index.lazy.tsx
The import path difference is due to an error in routes/index.lazy.tsx
which incorrectly imports ChatUI
from @/components/chat-list
. The chat-list component actually exports ChatList
, not ChatUI
. The import in routes/chats/index.tsx
is correct.
routes/index.lazy.tsx
: Changeimport ChatUI from "@/components/chat-list"
toimport ChatList from "@/components/chat-list"
Note: The chatId parameter is correctly handled via Route.useParams()
in the ChatUI component, no prop passing is required.
🔗 Analysis chain
Verify ChatUI component props and import consistency.
- The ChatUI import path (
@/components/chat-ui
) differs from the previous file (@/components/chat-list
) - Need to verify if ChatUI component accepts chatId prop
Please ensure consistent import paths and proper prop types.
Also applies to: 5-5
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check ChatUI component definition and props
echo "Searching for ChatUI component definition..."
rg -A 10 "export.*function.*ChatUI" src/components/
Length of output: 208
Script:
#!/bin/bash
# Find chat-related files
echo "=== Finding chat-related files ==="
fd -t f -i chat
# Look for path alias configuration
echo -e "\n=== Checking path alias configuration ==="
fd -t f -e json tsconfig
fd -t f -e js vite.config
fd -t f -e ts vite.config
# Find ChatUI component definition using AST
echo -e "\n=== Looking for ChatUI component definition ==="
ast-grep --pattern 'export $_ function ChatUI($_) { $$$ }'
ast-grep --pattern 'export $_ const ChatUI = ($props: $_) => { $$$ }'
# Find any imports of ChatUI
echo -e "\n=== Finding ChatUI imports ==="
rg "import.*ChatUI.*from"
Length of output: 1375
Script:
#!/bin/bash
# Examine both chat components
echo "=== Content of chat-ui.tsx ==="
cat "clients/example-ui/src/components/chat-ui.tsx"
echo -e "\n=== Content of chat-list.tsx ==="
cat "clients/example-ui/src/components/chat-list.tsx"
# Look for type definitions
echo -e "\n=== Looking for ChatUI related types ==="
rg "type.*Props" "clients/example-ui/src/components/chat-ui.tsx" "clients/example-ui/src/components/chat-list.tsx"
rg "interface.*Props" "clients/example-ui/src/components/chat-ui.tsx" "clients/example-ui/src/components/chat-list.tsx"
Length of output: 8288
export const useAppStore = create<AppState>()( | ||
devtools( | ||
(set: SetState, get: GetState) => ({ | ||
// Theme | ||
theme: (localStorage.getItem('theme') as 'light' | 'dark') || 'light', | ||
toggleTheme: () => | ||
set((state: AppState) => { | ||
const newTheme = state.theme === 'light' ? 'dark' : 'light'; | ||
localStorage.setItem('theme', newTheme); | ||
return { theme: newTheme }; | ||
}), | ||
|
||
// Debug | ||
showDebug: false, | ||
toggleShowDebug: () => set((state: AppState) => ({ showDebug: !state.showDebug })), | ||
|
||
// Messages | ||
messages: [], | ||
addMessage: (message: Message) => | ||
set((state: AppState) => ({ messages: [...state.messages, message] })), | ||
clearMessages: () => set({ messages: [] }), | ||
getMessagesForCurrentOrchestrator: () => { | ||
const state = get(); | ||
return state.messages.filter( | ||
(msg: Message) => | ||
msg.orchestratorId === state.currentOrchestratorId || | ||
msg.type === 'welcome' || | ||
msg.type === 'orchestrators_list' | ||
); | ||
}, | ||
|
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.
🛠️ Refactor suggestion
Add theme validation for localStorage.
Add type checking for the theme value from localStorage to ensure type safety.
+const isValidTheme = (theme: unknown): theme is Theme =>
+ typeof theme === 'string' && THEMES.includes(theme as Theme);
export const useAppStore = create<AppState>()(
devtools(
(set: SetState, get: GetState) => ({
// Theme
- theme: (localStorage.getItem('theme') as 'light' | 'dark') || 'light',
+ theme: ((): Theme => {
+ const stored = localStorage.getItem('theme');
+ return stored && isValidTheme(stored) ? stored : 'light';
+ })(),
📝 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.
export const useAppStore = create<AppState>()( | |
devtools( | |
(set: SetState, get: GetState) => ({ | |
// Theme | |
theme: (localStorage.getItem('theme') as 'light' | 'dark') || 'light', | |
toggleTheme: () => | |
set((state: AppState) => { | |
const newTheme = state.theme === 'light' ? 'dark' : 'light'; | |
localStorage.setItem('theme', newTheme); | |
return { theme: newTheme }; | |
}), | |
// Debug | |
showDebug: false, | |
toggleShowDebug: () => set((state: AppState) => ({ showDebug: !state.showDebug })), | |
// Messages | |
messages: [], | |
addMessage: (message: Message) => | |
set((state: AppState) => ({ messages: [...state.messages, message] })), | |
clearMessages: () => set({ messages: [] }), | |
getMessagesForCurrentOrchestrator: () => { | |
const state = get(); | |
return state.messages.filter( | |
(msg: Message) => | |
msg.orchestratorId === state.currentOrchestratorId || | |
msg.type === 'welcome' || | |
msg.type === 'orchestrators_list' | |
); | |
}, | |
export const useAppStore = create<AppState>()( | |
devtools( | |
(set: SetState, get: GetState) => ({ | |
// Theme | |
theme: ((): Theme => { | |
const stored = localStorage.getItem('theme'); | |
return stored && isValidTheme(stored) ? stored : 'light'; | |
})(), | |
toggleTheme: () => | |
set((state: AppState) => { | |
const newTheme = state.theme === 'light' ? 'dark' : 'light'; | |
localStorage.setItem('theme', newTheme); | |
return { theme: newTheme }; | |
}), | |
// Debug | |
showDebug: false, | |
toggleShowDebug: () => set((state: AppState) => ({ showDebug: !state.showDebug })), | |
// Messages | |
messages: [], | |
addMessage: (message: Message) => | |
set((state: AppState) => ({ messages: [...state.messages, message] })), | |
clearMessages: () => set({ messages: [] }), | |
getMessagesForCurrentOrchestrator: () => { | |
const state = get(); | |
return state.messages.filter( | |
(msg: Message) => | |
msg.orchestratorId === state.currentOrchestratorId || | |
msg.type === 'welcome' || | |
msg.type === 'orchestrators_list' | |
); | |
}, |
public async createOrchestrator(orchestratorData: OrchestratorData): Promise<ObjectId> { | ||
try { | ||
const result = await this.orchestratorCollection.insertOne(orchestratorData); | ||
console.log('✅ Orchestrator created:', result.insertedId); | ||
return result.insertedId; | ||
} catch (error) { | ||
console.error('❌ Error creating orchestrator:', error); | ||
throw error; | ||
} |
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.
Import missing type and enhance error handling.
The OrchestratorData
type is used but not imported. Also, consider adding more specific error types for better error handling.
+ import type { OrchestratorData } from "../types";
public async createOrchestrator(orchestratorData: OrchestratorData): Promise<ObjectId> {
try {
const result = await this.orchestratorCollection.insertOne(orchestratorData);
console.log('✅ Orchestrator created:', result.insertedId);
return result.insertedId;
} catch (error) {
- console.error('❌ Error creating orchestrator:', error);
- throw error;
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ console.error('❌ Error creating orchestrator:', errorMessage);
+ throw new Error(`Failed to create orchestrator: ${errorMessage}`);
}
}
📝 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.
public async createOrchestrator(orchestratorData: OrchestratorData): Promise<ObjectId> { | |
try { | |
const result = await this.orchestratorCollection.insertOne(orchestratorData); | |
console.log('✅ Orchestrator created:', result.insertedId); | |
return result.insertedId; | |
} catch (error) { | |
console.error('❌ Error creating orchestrator:', error); | |
throw error; | |
} | |
import type { OrchestratorData } from "../types"; | |
public async createOrchestrator(orchestratorData: OrchestratorData): Promise<ObjectId> { | |
try { | |
const result = await this.orchestratorCollection.insertOne(orchestratorData); | |
console.log('✅ Orchestrator created:', result.insertedId); | |
return result.insertedId; | |
} catch (error) { | |
const errorMessage = error instanceof Error ? error.message : String(error); | |
console.error('❌ Error creating orchestrator:', errorMessage); | |
throw new Error(`Failed to create orchestrator: ${errorMessage}`); | |
} |
packages/core/src/core/mongo-db.ts
Outdated
public async getOrchestratorById(orchestratorId: string): Promise<OrchestratorChat | null> { | ||
try { | ||
const orchestrator = await this.orchestratorCollection.findOne({ | ||
_id: new ObjectId(orchestratorId) | ||
}); | ||
return orchestrator; | ||
} catch (error) { | ||
console.error('❌ Error fetching orchestrator:', error); | ||
throw error; | ||
} |
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.
Fix type mismatch in getOrchestratorById.
The method accepts a string but uses it to create an ObjectId. This could fail if the string is not a valid ObjectId.
- public async getOrchestratorById(orchestratorId: string): Promise<OrchestratorChat | null> {
+ public async getOrchestratorById(orchestratorId: string | ObjectId): Promise<OrchestratorChat | null> {
try {
- const orchestrator = await this.orchestratorCollection.findOne({
- _id: new ObjectId(orchestratorId)
- });
+ const _id = typeof orchestratorId === 'string' ? new ObjectId(orchestratorId) : orchestratorId;
+ const orchestrator = await this.orchestratorCollection.findOne({ _id });
return orchestrator;
} catch (error) {
- console.error('❌ Error fetching orchestrator:', error);
- throw error;
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ console.error('❌ Error fetching orchestrator:', errorMessage);
+ throw new Error(`Failed to fetch orchestrator: ${errorMessage}`);
}
}
📝 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.
public async getOrchestratorById(orchestratorId: string): Promise<OrchestratorChat | null> { | |
try { | |
const orchestrator = await this.orchestratorCollection.findOne({ | |
_id: new ObjectId(orchestratorId) | |
}); | |
return orchestrator; | |
} catch (error) { | |
console.error('❌ Error fetching orchestrator:', error); | |
throw error; | |
} | |
public async getOrchestratorById(orchestratorId: string | ObjectId): Promise<OrchestratorChat | null> { | |
try { | |
const _id = typeof orchestratorId === 'string' ? new ObjectId(orchestratorId) : orchestratorId; | |
const orchestrator = await this.orchestratorCollection.findOne({ _id }); | |
return orchestrator; | |
} catch (error) { | |
const errorMessage = error instanceof Error ? error.message : String(error); | |
console.error('❌ Error fetching orchestrator:', errorMessage); | |
throw new Error(`Failed to fetch orchestrator: ${errorMessage}`); | |
} | |
} |
const existingOrchestrator = await this.mongoDb.getOrchestratorById( | ||
new ObjectId(orchestratorId) | ||
); | ||
|
||
if (!existingOrchestrator) { | ||
orchestratorId = await this.mongoDb.createOrchestrator(userId); | ||
} |
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.
Remove duplicate code block and fix type mismatch.
This code block is duplicated from lines above and contains a type mismatch error.
- const existingOrchestrator = await this.mongoDb.getOrchestratorById(
- new ObjectId(orchestratorId)
- );
- if (!existingOrchestrator) {
- orchestratorId = await this.mongoDb.createOrchestrator(userId);
- }
Committable suggestion skipped: line range outside the PR's diff.
clients/example-ui/src/index.css
Outdated
@keyframes gradient { | ||
0% { | ||
background-position: 0% 50%; | ||
} | ||
50% { | ||
background-position: 100% 50%; | ||
} | ||
100% { | ||
background-position: 0% 50%; | ||
} | ||
} |
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.
Remove duplicate gradient animation keyframes
The gradient
animation keyframes are defined twice in the file. Remove the duplicate definition at lines 270-274.
Apply this diff to remove the duplicate:
-@keyframes gradient {
- 0% { background-position: 0% 50%; }
- 50% { background-position: 100% 50%; }
- 100% { background-position: 0% 50%; }
-}
Also applies to: 270-274
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.
Actionable comments posted: 9
🧹 Nitpick comments (10)
examples/example-server.ts (3)
220-227
: Consider using environment variables for CORS configuration.The CORS configuration is currently hardcoded with development origins. For better flexibility across environments, consider using environment variables.
const corsOptions = { - origin: ['http://localhost:5173', 'http://localhost:3000'], + origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:5173', 'http://localhost:3000'], methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, optionsSuccessStatus: 200 };
241-244
: Avoid usingany
type.Using
any
type reduces type safety. Define a proper type for the query object.- let query: any = { userId: userId.toString() }; + interface HistoryQuery { + userId: string; + _id?: ObjectId; + } + let query: HistoryQuery = { userId: userId.toString() };
398-446
: Add input validation using zod schemas.Consider using zod schemas for request validation to ensure data consistency and improve error handling.
const CreateOrchestratorSchema = z.object({ name: z.string().optional(), userId: z.string(), model: z.string().optional(), temperature: z.number().min(0).max(1).optional() }); app.post("/api/orchestrators", async (req, res) => { try { const validatedData = CreateOrchestratorSchema.parse(req.body); // ... rest of the implementation } catch (error) { if (error instanceof z.ZodError) { return res.status(400).json({ error: "Invalid input", details: error.errors }); } // ... rest of error handling } });clients/example-ui/src/store/use-app-store.ts (2)
19-23
: Consider adding doc comments for Orchestrator interface
This interface could benefit from brief doc comments describing each field’s meaning. Enhancing documentation supports code readability and maintainability, especially for new team members.
81-94
: Prevent unlimited message growth
When usingmessages
as a simple array, it can grow large if users never clear them. Consider implementing a cap or clean-up mechanism to avoid performance or memory issues over time.clients/example-ui/src/components/collapsible-json.tsx (1)
17-31
: Watch out for potential performance impacts in deep structures
UsinguseState
to store collapsed keys works well for modestly sized data. For extremely large or deeply nested JSON objects, consider optimizing or lazy-rendering to avoid performance degradation.clients/example-ui/src/components/message-list.tsx (2)
21-83
: Consider using CSS variables for better maintainability.The message styling system is well-structured, but the hardcoded color values could be moved to CSS variables for better maintainability and theme support.
Consider refactoring like this:
- text-[#FF307B] - shadow-[0_0_30px_rgba(255,48,123,0.3)] - border border-[#FF307B]/30 + text-[var(--message-input-color)] + shadow-[0_0_30px_var(--message-input-shadow)] + border border-[var(--message-input-border)]Then define these variables in your CSS:
:root { --message-input-color: #FF307B; --message-input-shadow: rgba(255,48,123,0.3); --message-input-border: rgba(255,48,123,0.3); /* Add other message type variables */ }
113-117
: Consider timezone handling for timestamps.The current timestamp implementation uses local time. Consider adding explicit timezone handling for consistency across different user locations.
Example enhancement:
- {time && ( + {time && ( <div className="text-xs opacity-50 absolute bottom-1 right-2"> - {time} + {new Date(msg.timestamp!).toLocaleTimeString(undefined, { + timeZone: 'UTC', + hour12: false, + })} </div> )}clients/example-ui/src/components/chat-ui.tsx (2)
23-29
: Consider memoizing derived state.The component uses multiple state values from useAppStore. Consider using useMemo for derived values to optimize performance.
Example optimization:
+ const memoizedStoreValues = React.useMemo(() => ({ + currentOrchestratorId, + showDebug, + messages: storeMessages, + }), [currentOrchestratorId, showDebug, storeMessages]);
100-105
: Consider enhancing loading state feedback.While the Blade Runner quotes are creative, consider adding more context about what's happening during the loading state.
Example enhancement:
if (isLoading) { interval = setInterval(() => { setQuoteIndex((prev) => (prev + 1) % bladerunnerQuotes.length); + // Add loading progress indication + const loadingStates = ['Connecting to AI', 'Processing request', 'Generating response']; + setLoadingState((prev) => (prev + 1) % loadingStates.length); }, 3000); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
clients/example-ui/src/components/chat-ui.tsx
(2 hunks)clients/example-ui/src/components/collapsible-json.tsx
(1 hunks)clients/example-ui/src/components/debug-panel.tsx
(1 hunks)clients/example-ui/src/components/message-list.tsx
(1 hunks)clients/example-ui/src/store/use-app-store.ts
(1 hunks)examples/example-server.ts
(7 hunks)
🔇 Additional comments (9)
examples/example-server.ts (2)
34-43
: LGTM! Well-structured parameter handling.The function now accepts optional configuration parameters with sensible defaults, improving its flexibility while maintaining backward compatibility.
143-172
: User ownership check recommended.When retrieving orchestrators, ensure you verify that the orchestrator belongs to the requesting user to prevent unauthorized access.
clients/example-ui/src/store/use-app-store.ts (3)
65-75
: Add type-checking for localStorage theme value
It appears that an older review already suggested using a validation function to ensure that the theme retrieved from localStorage is either'light'
or'dark'
. Consider implementing that to avoid unexpected values or errors.
111-132
: Add orchestrator data validation & dev-only logging
A prior review recommended validating orchestrator objects and restricting logging to development builds. This helps prevent invalid data insertion and avoids leaking sensitive details in production logs.
134-136
: Toggle debug state looks good
ThetoggleDebug
method is straightforward and consistent with other toggle patterns in the store.clients/example-ui/src/components/collapsible-json.tsx (1)
47-98
: Keen object/array handling
Your recursive rendering for objects and arrays is well structured. Including a filter forEXCLUDED_KEYS
keeps the output clean. This approach effectively balances readability and functionality.clients/example-ui/src/components/debug-panel.tsx (1)
7-24
: Debug panel layout looks solid
The overall structure, with a collapsible JSON view and a property count, is a user-friendly way to inspect the app’s Zustand state.clients/example-ui/src/components/message-list.tsx (2)
15-19
: Well-implemented scroll behavior!The smooth scrolling implementation using useRef and useEffect is clean and follows React best practices.
4-7
: Verify type consistency across components.The message types have been standardized to uppercase, but we need to ensure consistency with other components. Additionally, consider adding runtime validation for the timestamp property.
Let's verify the message type usage across the codebase:
✅ Verification successful
Message types are consistently used across components
The uppercase message types are properly defined and consistently used throughout the UI components and core types. The found lowercase occurrences are unrelated to message types (theme settings, HTML elements, etc.).
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for message type definitions and usages echo "Searching for message type definitions..." rg -g '*.{ts,tsx}' '"(INPUT|OUTPUT|SYSTEM|ERROR|OTHER|WELCOME|INFO)"' echo "Searching for potential lowercase type usages..." rg -g '*.{ts,tsx}' '"(input|output|system|error|other|welcome|info)"'Length of output: 4470
// Si aucun historique n'est trouvé et qu'aucun orchestratorId n'est spécifié, | ||
// créer un orchestrateur par défaut | ||
if (histories.length === 0 && !orchestratorId) { | ||
console.log(chalk.yellow("[API] No histories found, creating default orchestrator")); | ||
|
||
const id = new ObjectId(); | ||
const defaultOrchestrator = { | ||
_id: id, | ||
name: "Default Orchestrator", | ||
userId: userId.toString(), | ||
messages: [{ | ||
role: "system", | ||
name: "system", | ||
data: { | ||
content: "Default orchestrator initialized", | ||
userId | ||
}, | ||
timestamp: new Date() | ||
}], | ||
createdAt: new Date(), | ||
updatedAt: new Date() | ||
}; | ||
|
||
await scheduledTaskDb.collection.insertOne(defaultOrchestrator); | ||
histories = [defaultOrchestrator]; | ||
console.log(chalk.green("[API] Created default orchestrator:", id.toString())); | ||
} |
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.
🛠️ Refactor suggestion
Extract default orchestrator creation logic to a helper function.
The default orchestrator creation logic is duplicated across multiple endpoints. Consider extracting it to a reusable helper function.
async function createDefaultOrchestrator(userId: string) {
const id = new ObjectId();
const defaultOrchestrator = {
_id: id,
name: "Default Orchestrator",
userId: userId.toString(),
messages: [{
role: "system",
name: "system",
data: {
content: "Default orchestrator initialized",
userId
},
timestamp: new Date()
}],
createdAt: new Date(),
updatedAt: new Date()
};
await scheduledTaskDb.collection.insertOne(defaultOrchestrator);
return defaultOrchestrator;
}
async function createOrchestratorFromDb(orchestratorData: OrchestratorData) { | ||
if (!orchestratorData) { | ||
throw new Error('Cannot create orchestrator from null data'); | ||
} | ||
|
||
const loglevel = LogLevel.INFO; | ||
console.log('🔍 Creating orchestrator from data:', orchestratorData); | ||
|
||
const llmClient = new LLMClient({ | ||
model: orchestratorData.model || "anthropic/claude-3-5-sonnet-latest", | ||
temperature: orchestratorData.temperature || 0.3, | ||
}); | ||
|
||
const vectorDb = new ChromaVectorDB("agent_memory", { | ||
chromaUrl: "http://localhost:8000", | ||
logLevel: loglevel, | ||
}); | ||
|
||
const roomManager = new RoomManager(vectorDb); | ||
const processor = new MessageProcessor(llmClient, defaultCharacter, loglevel); | ||
|
||
const orchestrator = new Orchestrator( | ||
roomManager, | ||
vectorDb, | ||
[processor], | ||
scheduledTaskDb, | ||
{ | ||
level: loglevel, | ||
enableColors: true, | ||
enableTimestamp: true, | ||
} | ||
); | ||
|
||
orchestrator.registerIOHandler({ | ||
name: "user_chat", | ||
role: HandlerRole.INPUT, | ||
schema: z.object({ | ||
content: z.string(), | ||
userId: z.string().optional(), | ||
}), | ||
handler: async (payload) => payload, | ||
}); | ||
|
||
orchestrator.registerIOHandler({ | ||
name: "chat_reply", | ||
role: HandlerRole.OUTPUT, | ||
schema: z.object({ | ||
userId: z.string().optional(), | ||
message: z.string(), | ||
}), | ||
handler: async (payload) => { | ||
const { userId, message } = payload as { | ||
userId?: string; | ||
message: string; | ||
}; | ||
console.log(`Reply to user ${userId ?? "??"}: ${message}`); | ||
}, | ||
}); | ||
|
||
return orchestrator; | ||
} |
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.
🛠️ Refactor suggestion
Extract handler registration logic to a shared function.
The handler registration logic is duplicated between createDaydreamsAgent
and createOrchestratorFromDb
. Consider extracting it to a shared function.
function registerDefaultHandlers(orchestrator: Orchestrator) {
orchestrator.registerIOHandler({
name: "user_chat",
role: HandlerRole.INPUT,
schema: z.object({
content: z.string(),
userId: z.string().optional(),
}),
handler: async (payload) => payload,
});
orchestrator.registerIOHandler({
name: "chat_reply",
role: HandlerRole.OUTPUT,
schema: z.object({
userId: z.string().optional(),
message: z.string(),
}),
handler: async (payload) => {
const { userId, message } = payload as {
userId?: string;
message: string;
};
console.log(`Reply to user ${userId ?? "??"}: ${message}`);
},
});
}
if (orchestratorId) { | ||
const orchestratorData = await scheduledTaskDb.getOrchestratorById(orchestratorId); | ||
|
||
if (!orchestratorData) { | ||
throw new Error(`Orchestrator not found with ID: ${orchestratorId}`); | ||
} | ||
|
||
currentOrchestrator = await createOrchestratorFromDb(orchestratorData as OrchestratorData); |
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.
Add ObjectId validation for orchestratorId.
The orchestratorId should be validated before using it to prevent runtime errors.
Add validation before accessing the database:
if (orchestratorId) {
+ let objectId;
+ try {
+ objectId = new ObjectId(orchestratorId);
+ } catch (err) {
+ throw new Error("Invalid orchestrator ID format");
+ }
- const orchestratorData = await scheduledTaskDb.getOrchestratorById(orchestratorId);
+ const orchestratorData = await scheduledTaskDb.getOrchestratorById(objectId);
Committable suggestion skipped: line range outside the PR's diff.
export interface Message { | ||
type: string; | ||
message?: string; | ||
error?: string; | ||
orchestratorId?: string; | ||
timestamp?: number; | ||
messageType?: string; | ||
data?: any; | ||
orchestrators?: Array<{ | ||
id: string; | ||
name: string; | ||
}>; | ||
} |
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.
🛠️ Refactor suggestion
Prefer stronger typing for message data
Currently, data?: any;
in the Message
interface can make it harder to ensure type safety throughout the application. Defining a dedicated type or interface for data
will improve clarity, correctness, and maintainability.
interface CollapsibleJsonProps { | ||
data: any; | ||
level?: number; | ||
isRoot?: boolean; | ||
} |
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.
🛠️ Refactor suggestion
Avoid using any
for JSON data
Specifying any
for data
can lead to runtime conflicts. If you know the structure of your JSON, consider creating a more precise type or using a generic type parameter for better type safety.
interface DebugPanelProps { | ||
state: Record<string, any>; | ||
} |
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.
🛠️ Refactor suggestion
Refine the state
prop type
Using Record<string, any>
is flexible but may be too broad. If possible, provide a more explicit type or structure for the state
prop to ensure type safety and clarity across the app.
{msg.type !== "input" && msg.type !== "output" && ( | ||
<div className="text-xs font-semibold mb-2 opacity-70"> | ||
{msg.type} | ||
</div> |
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.
Fix case mismatch in type comparison.
The type comparison uses lowercase ("input", "output") while the MessageType interface uses uppercase types ("INPUT", "OUTPUT"). This will cause the type to always display.
Apply this fix:
- {msg.type !== "input" && msg.type !== "output" && (
+ {msg.type !== "INPUT" && msg.type !== "OUTPUT" && (
📝 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.
{msg.type !== "input" && msg.type !== "output" && ( | |
<div className="text-xs font-semibold mb-2 opacity-70"> | |
{msg.type} | |
</div> | |
{msg.type !== "INPUT" && msg.type !== "OUTPUT" && ( | |
<div className="text-xs font-semibold mb-2 opacity-70"> | |
{msg.type} | |
</div> |
if (messages.length > 0) { | ||
const lastMessage = messages[messages.length - 1]; | ||
const isMessageInStore = storeMessages.some( | ||
msg => msg.message === lastMessage.message && msg.type === lastMessage.type | ||
); | ||
setAllMessages(formattedMessages); | ||
|
||
if (!isMessageInStore) { | ||
addMessage({ | ||
...lastMessage, | ||
timestamp: Date.now() | ||
}); | ||
} |
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.
🛠️ Refactor suggestion
Optimize message processing logic.
The current message deduplication logic could be improved, and there's a potential for race conditions with async state updates.
Consider this optimization:
- const isMessageInStore = storeMessages.some(
- msg => msg.message === lastMessage.message && msg.type === lastMessage.type
- );
+ const isMessageInStore = storeMessages.some(msg =>
+ msg.message === lastMessage.message &&
+ msg.type === lastMessage.type &&
+ msg.timestamp === lastMessage.timestamp
+ );
- if (!isMessageInStore) {
+ if (!isMessageInStore && lastMessage.message) {
addMessage({
...lastMessage,
- timestamp: Date.now()
+ timestamp: lastMessage.timestamp || Date.now()
});
}
📝 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.
if (messages.length > 0) { | |
const lastMessage = messages[messages.length - 1]; | |
const isMessageInStore = storeMessages.some( | |
msg => msg.message === lastMessage.message && msg.type === lastMessage.type | |
); | |
setAllMessages(formattedMessages); | |
if (!isMessageInStore) { | |
addMessage({ | |
...lastMessage, | |
timestamp: Date.now() | |
}); | |
} | |
if (messages.length > 0) { | |
const lastMessage = messages[messages.length - 1]; | |
const isMessageInStore = storeMessages.some(msg => | |
msg.message === lastMessage.message && | |
msg.type === lastMessage.type && | |
msg.timestamp === lastMessage.timestamp | |
); | |
if (!isMessageInStore && lastMessage.message) { | |
addMessage({ | |
...lastMessage, | |
timestamp: lastMessage.timestamp || Date.now() | |
}); | |
} |
try { | ||
await sendGoal( | ||
input, | ||
currentOrchestratorId, | ||
currentChat?.userId, | ||
); | ||
} catch (error) { | ||
console.error('Failed to send message:', error); | ||
addMessage({ | ||
type: "ERROR", | ||
error: "Failed to send message", | ||
timestamp: Date.now(), | ||
}); | ||
} |
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.
🛠️ Refactor suggestion
Enhance error handling and user feedback.
The current error handling could be improved with more specific error messages and a retry mechanism.
Consider implementing:
try {
await sendGoal(
input,
currentOrchestratorId,
currentChat?.userId,
);
} catch (error) {
- console.error('Failed to send message:', error);
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
+ console.error('Failed to send message:', errorMessage);
addMessage({
type: "ERROR",
- error: "Failed to send message",
+ error: `Failed to send message: ${errorMessage}`,
timestamp: Date.now(),
});
+ // Add retry button to the UI
+ setRetryMessage({ input, orchestratorId: currentOrchestratorId });
}
Committable suggestion skipped: line range outside the PR's diff.
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.
Actionable comments posted: 4
🧹 Nitpick comments (15)
clients/example-ui/src/hooks/use-orchestrators.tsx (4)
5-5
: Add environment variable validation.The API_URL fallback could lead to unexpected behavior in production. Consider adding runtime validation for the environment variable.
-const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8081'; +const API_URL = (() => { + const url = import.meta.env.VITE_API_URL; + if (!url && import.meta.env.PROD) { + throw new Error('VITE_API_URL is required in production'); + } + return url || 'http://localhost:8081'; +})();
7-12
: Consider using a custom error type.The error state could be more specific to handle different types of errors (network, validation, etc.).
+type OrchestratorError = { + type: 'network' | 'validation' | 'unknown'; + message: string; +}; interface UseOrchestratorsReturn { loading: boolean; - error: string | null; + error: OrchestratorError | null; createOrchestrator: (name: string) => Promise<void>; fetchOrchestrators: () => Promise<void>; }
14-18
: Consider memoizing store values.To prevent unnecessary re-renders, consider memoizing the store values using separate selectors.
-const { setOrchestrators, orchestrators, setCurrentOrchestratorId, currentOrchestratorId } = useAppStore(); +const orchestrators = useAppStore(state => state.orchestrators); +const currentOrchestratorId = useAppStore(state => state.currentOrchestratorId); +const setOrchestrators = useAppStore(state => state.setOrchestrators); +const setCurrentOrchestratorId = useAppStore(state => state.setCurrentOrchestratorId);
52-55
: Remove debug logging.Remove the console.log statement or wrap it in a development check.
useEffect(() => { - console.log('🚀 useOrchestrators mounted'); fetchOrchestrators(); }, [fetchOrchestrators]);
packages/core/src/core/mongo-db.ts (6)
24-24
: Consider optional or defaulted fields for flexibility.
If "model" or "temperature" might be optional or subject to default values in the future, consider marking them as optional or providing default values to improve resilience in data handling.
30-31
: Validate or default "model" & "temperature" fields.
Explicitly confirming that these fields are always defined can prevent undefined or null assignments at runtime. If your code can handle defaults (e.g., a default model name), consider setting them here.
70-70
: Hardcoded collection name.
“orchestrators” is used directly. If you foresee multi-tenant or environment-based naming, consider allowing external configuration or constants for better maintainability.
218-226
: Augment error handling for consistency.
You currently log and re-throw the original error. Wrapping it with an application-specific message (e.g.,throw new Error("Failed to create orchestrator: " + error)
) can improve stack tracing and clarity.
243-258
: Consider message array growth.
Appending messages works well for smaller volumes. For heavier usage, storing messages separately or imposing retention policies can improve query performance and reduce document bloat.
286-295
: Validate ObjectId from string.
IforchestratorId
is invalid,new ObjectId(orchestratorId)
will throw. Consider validating with a helper before conversion, or catch the specific cast error for more user-friendly feedback.packages/core/src/core/orchestrator.ts (1)
282-289
: Centralize defaults for orchestrator creation.
The hardcoded"default"
model and0.7
temperature might be better managed via config or constants for easier updates and clarity.clients/example-ui/src/components/message-list.tsx (2)
21-83
: Consider using CSS variables for consistent themingThe hardcoded color values could be replaced with CSS variables defined in index.css for better maintainability and theme consistency.
Example for the INPUT case:
- text-[#FF307B] - shadow-[0_0_30px_rgba(255,48,123,0.3)] - border border-[#FF307B]/30 + text-[hsl(var(--message-user-text))] + shadow-[0_0_30px_hsl(var(--message-user-from)/0.3)] + border border-[hsl(var(--message-user-from))/0.3]
89-89
: Consider extracting timestamp formatting logicThe timestamp formatting could be extracted into a utility function for better maintainability and reuse.
+const formatMessageTime = (timestamp?: number) => + timestamp ? new Date(timestamp).toLocaleTimeString() : null; -const time = msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : null; +const time = formatMessageTime(msg.timestamp);clients/example-ui/src/index.css (2)
6-62
: Consider documenting the color palette design systemThe theme implementation is solid, but adding documentation about the color palette design system would help maintain consistency in future updates.
Add a comment block explaining the color system:
/* Color Palette Design System: - Primary Actions: Cyan électrique (--primary) - Secondary Actions: Violet néon (--secondary) - Accents: Rose néon (--accent) - Messages: - User: Cyan électrique - Assistant: White/Dark contrast - System: Violet néon - Error: Rouge néon - Welcome: Vert néon - Info: Cyan électrique */Also applies to: 65-120
226-267
: Consider extracting common message stylesThe message classes have repeated shadow and border patterns that could be extracted into a common utility class.
+ .message-base { + @apply shadow-lg border font-medium + transition-all duration-300 hover:brightness-110; + } .message-user { - @apply bg-gradient-to-r from-[hsl(var(--message-user-from))] to-[hsl(var(--message-user-to))] - text-[hsl(var(--message-user-text))] mr-2 self-end hover:brightness-110 - shadow-lg shadow-[hsl(var(--message-user-from))]/20 - border border-[hsl(var(--message-user-from))]/30 font-medium; + @apply message-base + bg-gradient-to-r from-[hsl(var(--message-user-from))] to-[hsl(var(--message-user-to))] + text-[hsl(var(--message-user-text))] mr-2 self-end + shadow-[hsl(var(--message-user-from))]/20 + border-[hsl(var(--message-user-from))]/30; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
clients/example-ui/src/components/message-list.tsx
(1 hunks)clients/example-ui/src/hooks/use-orchestrators.tsx
(1 hunks)clients/example-ui/src/index.css
(3 hunks)clients/example-ui/src/routeTree.gen.ts
(6 hunks)clients/example-ui/src/routes/index.lazy.tsx
(1 hunks)examples/types.ts
(1 hunks)packages/core/src/core/mongo-db.ts
(5 hunks)packages/core/src/core/orchestrator.ts
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- clients/example-ui/src/routes/index.lazy.tsx
- examples/types.ts
🔇 Additional comments (17)
clients/example-ui/src/routeTree.gen.ts (8)
Line range hint
1-15
: Note: This is an auto-generated file.This file is automatically generated by TanStack Router and should not be manually modified. Any changes made to this file will be overwritten.
16-16
: LGTM!The import follows the established pattern and maintains consistency with other route imports.
32-36
: LGTM!The route definition is well-structured and follows the established pattern, with correct parent route configuration.
75-81
: LGTM!The interface updates are consistent across all route-related interfaces, maintaining type safety and routing structure.
Also applies to: 91-91, 98-98, 106-106
111-111
: LGTM!The route types are properly updated with the new route path, maintaining type consistency.
Also applies to: 113-114
122-122
: LGTM!The root route children are properly updated in both the interface and implementation.
Also applies to: 129-129
144-145
: LGTM!The route manifest is correctly updated with the new route and its file path.
Also applies to: 157-159
Line range hint
1-161
: Verify the route implementation.Since this is an auto-generated file, let's verify that the route implementation exists and is properly exported.
✅ Verification successful
Route implementation is correctly configured ✅
The route is properly implemented using TanStack Router's
createFileRoute
with the correct path and component configuration.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that the route implementation exists and exports the required components # Check if the route file exists if ! fd -t f "index.tsx" "clients/example-ui/src/routes/my-agents/"; then echo "Error: Route file not found" exit 1 fi # Check if the file exports the Route component rg -l "export\s+const\s+Route" "clients/example-ui/src/routes/my-agents/index.tsx"Length of output: 243
Script:
#!/bin/bash # Check the route implementation details ast-grep --pattern 'export const Route = createFileRoute("/my-agents/")($$$)' clients/example-ui/src/routes/my-agents/index.tsx # Show the complete route implementation for manual verification rg -A 10 'export const Route' clients/example-ui/src/routes/my-agents/index.tsxLength of output: 296
clients/example-ui/src/hooks/use-orchestrators.tsx (1)
89-95
: LGTM!The hook's return value is well-structured and matches the interface definition perfectly.
packages/core/src/core/mongo-db.ts (2)
37-37
: Typed collection is good practice.
Specifying the generic typeOrchestratorData
prevents accidental misuse of the collection. Ensureconnect()
is called before using this property to avoid runtime errors.
279-279
: Clean approach returning OrchestratorData.
ReturningPromise<OrchestratorData[]>
ensures consistent type usage and clarity for consumers. Looks good.packages/core/src/core/orchestrator.ts (3)
8-8
: Good import for stronger typing.
ImportingOrchestratorData
directly from"./mongo-db"
ensures consistent usage across the codebase.
278-278
: ValidateorchestratorId
before.toString()
.
IforchestratorId
isundefined
or null, calling.toString()
will throw. Consider a guard or fallback scenario to avoid runtime errors.
335-335
: Simple debug output is fine.
Logging the orchestrator ID helps trace workflow. No issues found here.clients/example-ui/src/components/message-list.tsx (3)
1-8
: LGTM! Type system improvementsThe MessageType interface now uses consistent uppercase literals and includes timestamp support, improving type safety.
15-20
: LGTM! Smooth scroll behavior implementationGood use of useRef and useEffect for implementing smooth scroll behavior when new messages arrive.
91-91
: Avoid using array indices as keysUsing array indices as keys can lead to issues with component rerendering and state management when the list is dynamic (items are added/removed/reordered).
Consider adding a unique identifier to your message objects:
-<div key={i} className={containerClass}> +<div key={`${msg.timestamp}-${i}`} className={containerClass}>Let's verify if messages have any unique identifiers:
✅ Verification successful
Avoid using array indices as keys - Verification Complete
The review comment is valid. The code currently uses array indices as keys (
key={i}
), while the messages have a timestamp field that can be used to create more stable keys. The suggested solution to use${msg.timestamp}-${i}
is appropriate as it:
- Creates unique keys even when messages have the same timestamp
- Gracefully handles cases where timestamp is undefined
- Helps prevent rendering issues when messages are reordered or deleted
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for message object creation or type definitions ast-grep --pattern 'interface MessageType { $$$ }'Length of output: 546
Script:
#!/bin/bash # Search for message list rendering and message creation ast-grep --pattern 'messages.map((msg, i) => { $$$ })' # Also search for any message object creation rg "type.*?(?:INPUT|OUTPUT|SYSTEM|ERROR|OTHER|WELCOME|INFO).*?timestamp" -A 3Length of output: 3564
const fetchOrchestrators = useCallback(async () => { | ||
console.log('🔄 Fetching orchestrators...'); | ||
try { | ||
setLoading(true); | ||
const userId = generateUserId(); | ||
const response = await fetch( | ||
`${API_URL}/api/orchestrators?userId=${userId}` | ||
); | ||
|
||
if (!response.ok) { | ||
throw new Error(`HTTP error! status: ${response.status}`); | ||
} | ||
|
||
const data = await response.json(); | ||
console.log('📥 Received orchestrators:', data); | ||
setOrchestrators(data); | ||
|
||
if (!currentOrchestratorId && data.length > 0) { | ||
console.log('🎯 Setting default orchestrator:', data[0].id); | ||
setCurrentOrchestratorId(data[0].id); | ||
} | ||
|
||
setError(null); | ||
} catch (err) { | ||
console.error('❌ Error fetching orchestrators:', err); | ||
setError( | ||
err instanceof Error ? err.message : "Failed to fetch orchestrators" | ||
); | ||
} finally { | ||
setLoading(false); | ||
} | ||
}, [setOrchestrators, currentOrchestratorId, setCurrentOrchestratorId]); |
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.
🛠️ Refactor suggestion
Multiple improvements needed in fetchOrchestrators.
- Remove or guard console.log statements
- Consider caching userId
- Add type safety for API response
+interface Orchestrator {
+ id: string;
+ name: string;
+ userId: string;
+}
const fetchOrchestrators = useCallback(async () => {
- console.log('🔄 Fetching orchestrators...');
+ const userId = useMemo(() => generateUserId(), []); // Move to component level
try {
setLoading(true);
- const userId = generateUserId();
const response = await fetch(
`${API_URL}/api/orchestrators?userId=${userId}`
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
- const data = await response.json();
- console.log('📥 Received orchestrators:', data);
+ const data: Orchestrator[] = await response.json();
setOrchestrators(data);
if (!currentOrchestratorId && data.length > 0) {
- console.log('🎯 Setting default orchestrator:', data[0].id);
setCurrentOrchestratorId(data[0].id);
}
Committable suggestion skipped: line range outside the PR's diff.
const createOrchestrator = useCallback(async (name: string) => { | ||
try { | ||
setLoading(true); | ||
const response = await fetch(`${API_URL}/api/orchestrators`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
name, | ||
userId: generateUserId() | ||
}), | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error(`HTTP error! status: ${response.status}`); | ||
} | ||
|
||
const newOrchestrator = await response.json(); | ||
setOrchestrators([...orchestrators, newOrchestrator]); | ||
setCurrentOrchestratorId(newOrchestrator.id); | ||
setError(null); | ||
} catch (err) { | ||
setError( | ||
err instanceof Error ? err.message : "Failed to create orchestrator" | ||
); | ||
console.error("Error creating orchestrator:", err); | ||
} finally { | ||
setLoading(false); | ||
} | ||
}, [orchestrators, setOrchestrators, setCurrentOrchestratorId]); |
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.
🛠️ Refactor suggestion
Enhance error handling and add retry mechanism.
The error handling could be improved with retries for transient failures and better error classification.
+const MAX_RETRIES = 3;
+const RETRY_DELAY = 1000;
+async function wait(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
const createOrchestrator = useCallback(async (name: string) => {
+ let attempts = 0;
try {
setLoading(true);
- const response = await fetch(`${API_URL}/api/orchestrators`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- name,
- userId: generateUserId()
- }),
- });
+ while (attempts < MAX_RETRIES) {
+ try {
+ const response = await fetch(`${API_URL}/api/orchestrators`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ name,
+ userId: generateUserId()
+ }),
+ });
+ if (response.ok) {
+ const newOrchestrator: Orchestrator = await response.json();
+ setOrchestrators([...orchestrators, newOrchestrator]);
+ setCurrentOrchestratorId(newOrchestrator.id);
+ setError(null);
+ return;
+ }
+ const errorData = await response.json().catch(() => null);
+ throw new Error(errorData?.message || `HTTP error! status: ${response.status}`);
+ } catch (err) {
+ attempts++;
+ if (attempts === MAX_RETRIES) throw err;
+ await wait(RETRY_DELAY * attempts);
+ }
+ }
📝 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.
const createOrchestrator = useCallback(async (name: string) => { | |
try { | |
setLoading(true); | |
const response = await fetch(`${API_URL}/api/orchestrators`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
name, | |
userId: generateUserId() | |
}), | |
}); | |
if (!response.ok) { | |
throw new Error(`HTTP error! status: ${response.status}`); | |
} | |
const newOrchestrator = await response.json(); | |
setOrchestrators([...orchestrators, newOrchestrator]); | |
setCurrentOrchestratorId(newOrchestrator.id); | |
setError(null); | |
} catch (err) { | |
setError( | |
err instanceof Error ? err.message : "Failed to create orchestrator" | |
); | |
console.error("Error creating orchestrator:", err); | |
} finally { | |
setLoading(false); | |
} | |
}, [orchestrators, setOrchestrators, setCurrentOrchestratorId]); | |
const MAX_RETRIES = 3; | |
const RETRY_DELAY = 1000; | |
async function wait(ms: number) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
const createOrchestrator = useCallback(async (name: string) => { | |
let attempts = 0; | |
try { | |
setLoading(true); | |
while (attempts < MAX_RETRIES) { | |
try { | |
const response = await fetch(`${API_URL}/api/orchestrators`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
name, | |
userId: generateUserId() | |
}), | |
}); | |
if (response.ok) { | |
const newOrchestrator: Orchestrator = await response.json(); | |
setOrchestrators([...orchestrators, newOrchestrator]); | |
setCurrentOrchestratorId(newOrchestrator.id); | |
setError(null); | |
return; | |
} | |
const errorData = await response.json().catch(() => null); | |
throw new Error(errorData?.message || `HTTP error! status: ${response.status}`); | |
} catch (err) { | |
attempts++; | |
if (attempts === MAX_RETRIES) throw err; | |
await wait(RETRY_DELAY * attempts); | |
} | |
} | |
} catch (err) { | |
setError( | |
err instanceof Error ? err.message : "Failed to create orchestrator" | |
); | |
console.error("Error creating orchestrator:", err); | |
} finally { | |
setLoading(false); | |
} | |
}, [orchestrators, setOrchestrators, setCurrentOrchestratorId]); |
async getOrchestratorsByUserId(userId: string) { | ||
try { | ||
const collection = this.client | ||
.db(this.dbName) | ||
.collection("orchestrators"); | ||
|
||
return await collection | ||
.find({ userId }) | ||
.sort({ createdAt: -1 }) | ||
console.log('🔍 Fetching orchestrators for user:', userId); | ||
const orchestrators = await this.orchestratorCollection | ||
.find({ userId: userId.toString() }) | ||
.toArray(); | ||
//console.log('📦 Found orchestrators:', orchestrators); | ||
return orchestrators; | ||
} catch (error) { | ||
console.error( | ||
"MongoDb.getOrchestratorsByUserId", | ||
"Failed to fetch orchestrator records", | ||
{ userId, error } | ||
); | ||
console.error('❌ Error fetching orchestrators:', error); |
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.
🛠️ Refactor suggestion
Avoid duplicating logic for fetch-by-user.
The new method getOrchestratorsByUserId
appears similar to findOrchestratorsByUser
. Consider consolidating to reduce duplication and maintain a single source of truth.
const existingOrchestrator = await this.mongoDb.getOrchestratorById( | ||
orchestratorId.toString() | ||
); | ||
|
||
if (!existingOrchestrator) { | ||
orchestratorId = await this.mongoDb.createOrchestrator({ | ||
userId, | ||
createdAt: new Date(), | ||
updatedAt: new Date(), | ||
messages: [], | ||
model: "default", | ||
temperature: 0.7 | ||
} as OrchestratorData); | ||
} |
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.
🛠️ Refactor suggestion
Eliminate repeated "createOrchestrator" logic.
Lines 282-289 and 294-307 perform identical creation if the orchestrator is missing. Dry up repeated logic by extracting a helper method or reusing the existing block.
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.
Actionable comments posted: 1
🧹 Nitpick comments (7)
clients/example-ui/src/components/message-list.tsx (3)
21-60
: Consider enhancing type safety and maintainability of styling logic.While the styling implementation works, it could benefit from some improvements:
const getMessageClasses = (msg: MessageType) => { let containerClass = "flex w-full mb-4 px-4"; let bubbleClass = ` px-4 py-2 rounded-lg max-w-[80%] font-medium relative overflow-hidden bg-card text-foreground before:absolute before:inset-[1px] before:rounded-lg before:z-0 [&>*]:relative [&>*]:z-10 shadow-[0_0_30px_hsl(var(--primary)/0.05)] border border-[hsl(var(--primary))]/30 `; - switch (msg.type) { - case "INPUT": - containerClass += " justify-end"; - break; - - case "OUTPUT": - containerClass += " justify-start"; - break; - - case "SYSTEM": - containerClass += " justify-center"; - break; - - case "ERROR": - containerClass += " justify-start"; - break; - - default: - containerClass += " justify-start"; - } + const justifyMap: Record<MessageType['type'], string> = { + INPUT: 'justify-end', + OUTPUT: 'justify-start', + SYSTEM: 'justify-center', + ERROR: 'justify-start', + OTHER: 'justify-start', + WELCOME: 'justify-start', + INFO: 'justify-start' + }; + + containerClass += ` ${justifyMap[msg.type]}`; bubbleClass += ` hover:brightness-105 transition-all duration-300 `; return { containerClass, bubbleClass }; };This refactor:
- Ensures type safety by mapping all possible message types
- Eliminates the need for a default case
- Makes the styling more maintainable and centralized
64-94
: Consider improving message identification and time formatting.
- Using array indices as keys could cause issues with React's reconciliation if messages are reordered or deleted.
- Time formatting could be inconsistent across different locales.
Consider:
- Adding unique IDs to messages:
- {messages.map((msg, i) => { + {messages.map((msg) => { const { containerClass, bubbleClass } = getMessageClasses(msg); const time = msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : null; return ( - <div key={i} className={containerClass}> + <div key={msg.timestamp ?? msg.message} className={containerClass}>
- Using a consistent time formatting utility:
const formatTime = (timestamp: number) => { return new Intl.DateTimeFormat('default', { hour: 'numeric', minute: 'numeric', second: 'numeric' }).format(new Date(timestamp)); };
77-82
: Consider using a className utility for better maintainability.The className concatenation could be improved using a utility like
clsx
ortailwind-merge
.+ import { clsx } from 'clsx'; - <div className={` - break-words overflow-x-auto - ${time ? 'mb-4' : ''} - `}> + <div className={clsx( + 'break-words overflow-x-auto', + time && 'mb-4' + )}>clients/example-ui/src/index.css (4)
6-6
: Add theme name comment for consistency.Add a comment to document the light theme name "Theme Light - Néon urbain" for consistency with the dark theme documentation.
+ /* Theme Light - Néon urbain */
27-31
: Fix duplicate chart colors in light theme.Chart colors 1, 4, and 5 are using the same value (
221 83% 53%
). Consider using different colors for better visual distinction in charts.--chart-1: 221 83% 53%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; - --chart-4: 221 83% 53%; - --chart-5: 221 83% 53%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%;
181-221
: Refactor message styles to reduce duplication.The message components share many common styles. Consider extracting these into a base message class.
+ .message-base { + @apply bg-gradient-to-r shadow-lg font-medium + border border-opacity-30 shadow-opacity-20; + } + .message-user { - @apply bg-gradient-to-r from-[hsl(var(--message-user-from))] to-[hsl(var(--message-user-to))] - text-[hsl(var(--message-user-text))] mr-2 self-end hover:brightness-110 - shadow-lg shadow-[hsl(var(--message-user-from))]/20 - border border-[hsl(var(--message-user-from))]/30 font-medium; + @apply message-base + from-[hsl(var(--message-user-from))] to-[hsl(var(--message-user-to))] + text-[hsl(var(--message-user-text))] mr-2 self-end hover:brightness-110 + shadow-[hsl(var(--message-user-from))]/20 + border-[hsl(var(--message-user-from))]; } /* Apply similar changes to other message types */
188-221
: Add hover effects for consistency.Only the user message has a hover effect (
hover:brightness-110
). Consider adding hover effects to other message types for consistent interaction..message-assistant { @apply bg-gradient-to-r from-[hsl(var(--message-assistant-from))] to-[hsl(var(--message-assistant-to))] text-[hsl(var(--message-assistant-text))] ml-2 shadow-lg shadow-[hsl(var(--message-assistant-from))]/20 - border border-[hsl(var(--message-assistant-from))]/30 font-medium; + border border-[hsl(var(--message-assistant-from))]/30 font-medium + hover:brightness-110; } /* Add hover:brightness-110 to other message types */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
clients/example-ui/src/components/message-list.tsx
(1 hunks)clients/example-ui/src/index.css
(2 hunks)
🔇 Additional comments (3)
clients/example-ui/src/components/message-list.tsx (2)
1-8
: LGTM! Interface changes address previous type mismatch concerns.The MessageType interface has been properly updated with consistent uppercase types and the addition of an optional timestamp field improves message metadata support.
15-20
: Well-implemented scroll behavior!The scroll implementation using useRef and useEffect is clean and provides smooth UX. The dependency array is correctly configured to trigger on messages changes.
clients/example-ui/src/index.css (1)
167-177
: Remove duplicate gradient animation keyframes.The
gradient
animation keyframes are defined twice in the file. Remove the duplicate definition at lines 270-274.
clients/example-ui/src/index.css
Outdated
@layer components { | ||
.message-user { | ||
@apply bg-gradient-to-r from-[hsl(var(--message-user-from))] to-[hsl(var(--message-user-to))] | ||
text-[hsl(var(--message-user-text))] mr-2 self-end hover:brightness-110 | ||
shadow-lg shadow-[hsl(var(--message-user-from))]/20 | ||
border border-[hsl(var(--message-user-from))]/30 font-medium; | ||
} | ||
|
||
.message-assistant { | ||
@apply bg-gradient-to-r from-[hsl(var(--message-assistant-from))] to-[hsl(var(--message-assistant-to))] | ||
text-[hsl(var(--message-assistant-text))] ml-2 | ||
shadow-lg shadow-[hsl(var(--message-assistant-from))]/20 | ||
border border-[hsl(var(--message-assistant-from))]/30 font-medium; | ||
} | ||
|
||
.message-system { | ||
@apply bg-gradient-to-r from-[hsl(var(--message-system-from))] to-[hsl(var(--message-system-to))] | ||
text-[hsl(var(--message-system-text))] | ||
shadow-lg shadow-[hsl(var(--message-system-from))]/20 | ||
border border-[hsl(var(--message-system-from))]/30 font-medium; | ||
} | ||
|
||
.message-error { | ||
@apply bg-gradient-to-r from-[hsl(var(--message-error-from))] to-[hsl(var(--message-error-to))] | ||
text-[hsl(var(--message-error-text))] | ||
shadow-lg shadow-[hsl(var(--message-error-from))]/20 | ||
border border-[hsl(var(--message-error-from))]/30 font-medium; | ||
} | ||
|
||
.message-welcome { | ||
@apply bg-gradient-to-r from-[hsl(var(--message-welcome-from))] to-[hsl(var(--message-welcome-to))] | ||
text-[hsl(var(--message-welcome-text))] | ||
shadow-lg shadow-[hsl(var(--message-welcome-from))]/20 | ||
border border-[hsl(var(--message-welcome-from))]/30 font-medium; | ||
} | ||
|
||
.message-info { | ||
@apply bg-gradient-to-r from-[hsl(var(--message-info-from))] to-[hsl(var(--message-info-to))] | ||
text-[hsl(var(--message-info-text))] | ||
shadow-lg shadow-[hsl(var(--message-info-from))]/20 | ||
border border-[hsl(var(--message-info-from))]/30 font-medium; | ||
} |
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.
Define missing message-specific CSS custom properties.
The message components reference color variables that aren't defined in the theme (e.g., --message-user-from
, --message-assistant-from
). Add these to the theme definitions.
Add these variables to both light and dark themes:
:root {
+ /* Message - User */
+ --message-user-from: 221 83% 53%;
+ --message-user-to: 196 100% 45%;
+ --message-user-text: 0 0% 100%;
+
+ /* Message - Assistant */
+ --message-assistant-from: 43 96% 58%;
+ --message-assistant-to: 23 96% 58%;
+ --message-assistant-text: 0 0% 100%;
+
+ /* Message - System */
+ --message-system-from: 225 47% 15%;
+ --message-system-to: 225 47% 25%;
+ --message-system-text: 0 0% 100%;
+
+ /* Message - Error */
+ --message-error-from: 0 84% 60%;
+ --message-error-to: 0 84% 70%;
+ --message-error-text: 0 0% 100%;
+
+ /* Message - Welcome */
+ --message-welcome-from: 196 100% 45%;
+ --message-welcome-to: 196 100% 55%;
+ --message-welcome-text: 0 0% 100%;
+
+ /* Message - Info */
+ --message-info-from: 221 83% 53%;
+ --message-info-to: 221 83% 63%;
+ --message-info-text: 0 0% 100%;
Committable suggestion skipped: line range outside the PR's diff.
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.
Actionable comments posted: 0
🧹 Nitpick comments (3)
clients/example-ui/src/components/message-list.tsx (3)
21-82
: Consider extracting message style configurations.The message styling logic could be simplified and made more maintainable by extracting the style configurations.
Consider refactoring to use a configuration object:
+ const MESSAGE_STYLES = { + INPUT: { + container: "justify-end", + bubble: "bg-card text-foreground mr-2 self-end hover:brightness-110" + }, + OUTPUT: { + container: "justify-start", + bubble: "bg-card text-foreground ml-2 hover:brightness-105" + }, + // ... other types + }; + const getMessageClasses = (msg: MessageType) => { const baseBubble = ` relative p-4 text-sm shadow-md transition-all duration-200 w-[80%] whitespace-pre-wrap break-words border-opacity-50 dither-border `; let containerClass = "flex items-start mb-4 px-4"; - let bubbleClass = baseBubble; + const styles = MESSAGE_STYLES[msg.type] || MESSAGE_STYLES.default; + return { + containerClass: `${containerClass} ${styles.container}`, + bubbleClass: `${baseBubble} ${styles.bubble}` + }; - switch (msg.type) { - // ... remove switch statement - } - return { containerClass, bubbleClass }; };
88-88
: Extract timestamp formatting to a utility function.Consider moving the timestamp formatting logic to a reusable utility function for better maintainability and consistency.
+ const formatMessageTime = (timestamp?: number) => + timestamp ? new Date(timestamp).toLocaleTimeString() : null; export function MessagesList({ messages }: MessagesListProps) { // ... return ( <div className="flex flex-col w-full"> {messages.map((msg, i) => { const { containerClass, bubbleClass } = getMessageClasses(msg); - const time = msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : null; + const time = formatMessageTime(msg.timestamp);
99-104
: Simplify className template literal.The nested template literal with conditional classes could be simplified using a utility like clsx/classnames.
+ import clsx from 'clsx'; <div className={clsx( 'text-base break-words overflow-x-auto', time && 'mb-4' )}> {msg.message} </div>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
clients/example-ui/src/components/message-list.tsx
(1 hunks)clients/example-ui/src/index.css
(0 hunks)
💤 Files with no reviewable changes (1)
- clients/example-ui/src/index.css
🔇 Additional comments (3)
clients/example-ui/src/components/message-list.tsx (3)
4-7
: Verify type consistency across components.The MessageType interface has been updated to use uppercase literals. Ensure this change is reflected in all components that interact with messages.
#!/bin/bash # Search for message type references across the codebase echo "Searching for message type references..." rg -A 2 'type:.*("user"|"assistant"|"system"|"error"|"INPUT"|"OUTPUT"|"SYSTEM"|"ERROR")' # Search for message interfaces/types echo "Searching for message type definitions..." ast-grep --pattern 'interface MessageType { $$$ }'
15-19
: Well-implemented scroll behavior!The scroll-to-bottom functionality using useRef and useEffect is cleanly implemented with smooth scrolling behavior.
90-90
: Use stable keys for message items.Using array indices as keys can lead to issues with React's reconciliation when messages are reordered or deleted.
Consider adding a unique identifier to your messages or using a combination of timestamp and content for a more stable key.
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 3
♻️ Duplicate comments (2)
clients/example-ui/src/components/chat-ui.tsx (2)
39-50
: 🛠️ Refactor suggestionOptimize message deduplication logic.
The message deduplication logic could still be improved to prevent race conditions with async state updates.
Apply the previously suggested optimization:
const isMessageInStore = storeMessages.some(msg => msg.message === lastMessage.message && msg.type === lastMessage.type && + msg.timestamp === lastMessage.timestamp ); -if (!isMessageInStore) { +if (!isMessageInStore && lastMessage.message) { addMessage({ ...lastMessage, - timestamp: Date.now() + timestamp: lastMessage.timestamp || Date.now() }); }
83-96
: 🛠️ Refactor suggestionEnhance error handling and user feedback.
The error handling could be improved with more specific error messages and a retry mechanism.
Apply the previously suggested enhancement:
try { await sendGoal( input, currentOrchestratorId, currentChat?.userId, ); } catch (error) { - console.error('Failed to send message:', error); + const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; + console.error('Failed to send message:', errorMessage); addMessage({ type: "ERROR", - error: "Failed to send message", + error: `Failed to send message: ${errorMessage}`, timestamp: Date.now(), }); + setRetryMessage({ input, orchestratorId: currentOrchestratorId }); }
🛑 Comments failed to post (3)
clients/example-ui/src/components/chat-ui.tsx (3)
33-36: 🛠️ Refactor suggestion
Remove or document commented code.
The commented useParams hook suggests an incomplete transition from route-based to store-based chat identification. Additionally, there's no null check when finding the current chat.
Apply these changes:
- //const { chatId } = useParams({ from: '/chats/$chatId' }); const { chatItems, loading } = useChatHistories(); -const currentChat = chatItems.find(chat => chat._id === currentOrchestratorId); +const currentChat = chatItems.find(chat => chat._id === currentOrchestratorId) ?? null;📝 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.const { chatItems, loading } = useChatHistories(); const currentChat = chatItems.find(chat => chat._id === currentOrchestratorId) ?? null;
157-160:
⚠️ Potential issueLimit debug information exposure.
Passing the entire store state to the DebugPanel could potentially expose sensitive information in production.
Consider:
- Limiting debug functionality to development environment
- Filtering sensitive information from debug output
{showDebug && ( <DebugPanel messages={storeMessages} - state={useAppStore.getState()} + state={process.env.NODE_ENV === 'development' + ? filterSensitiveInfo(useAppStore.getState()) + : { messages: storeMessages }} /> )}Committable suggestion skipped: line range outside the PR's diff.
55-63: 🛠️ Refactor suggestion
Avoid direct state mutation with useAppStore.setState.
Direct state mutation using useAppStore.setState bypasses the store's action pattern and could lead to inconsistent state updates.
Use the addMessage action instead:
if (currentChat?.messages) { const formattedMessages = currentChat.messages.map(msg => ({ type: msg.role.toUpperCase(), message: msg.data.content || msg.data.message || "", timestamp: new Date(msg.timestamp).getTime(), })); - useAppStore.setState({ messages: formattedMessages }); + formattedMessages.forEach(msg => addMessage(msg)); }📝 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.if (currentChat?.messages) { //console.log('📝 Processing historical messages for chat:', chatId); const formattedMessages = currentChat.messages.map(msg => ({ type: msg.role.toUpperCase(), message: msg.data.content || msg.data.message || "", timestamp: new Date(msg.timestamp).getTime(), })); formattedMessages.forEach(msg => addMessage(msg)); }
Summary by CodeRabbit
Release Notes
New Features
Dependency Updates
UI/UX Improvements
Performance Optimizations
Bug Fixes