Skip to content
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

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

Cheelax
Copy link
Contributor

@Cheelax Cheelax commented Jan 29, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a new "My Agents" page for managing orchestrators.
    • Introduced a debug panel for inspecting application state.
    • Enhanced chat history and message management with improved navigation.
    • Implemented dynamic routing for chat and agent management.
    • Added a collapsible JSON component for better data visualization.
  • Dependency Updates

    • Added Zustand state management library.
    • Updated TypeScript configuration for React support.
  • UI/UX Improvements

    • Redesigned message styling with gradient backgrounds.
    • Improved chat interface and navigation.
    • Added theme toggling capabilities.
    • Enhanced utility classes and CSS custom properties for theming.
  • Performance Optimizations

    • Implemented lazy loading for route components.
    • Enhanced WebSocket and API error handling.
  • Bug Fixes

    • Improved orchestrator and chat history management.
    • Fixed routing and state synchronization issues.

Copy link

vercel bot commented Jan 29, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
daydreams ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 30, 2025 2:33pm

Copy link
Contributor

coderabbitai bot commented Jan 29, 2025

Walkthrough

This 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

File Change Summary
clients/example-ui/package.json Added zustand dependency
clients/example-ui/src/components/app-sidebar.tsx Updated navigation and chat history selection logic
clients/example-ui/src/components/chat-list.tsx New component for displaying chat histories
clients/example-ui/src/components/chat-ui.tsx Refactored to use application store for state management
clients/example-ui/src/components/debug-panel.tsx New component for displaying application state
clients/example-ui/src/components/message-list.tsx Updated message type definitions and styling
clients/example-ui/src/components/my-agents-page.tsx New component for managing orchestrators
clients/example-ui/src/hooks/use-chat-histories.tsx Enhanced chat histories hook with loading and error states
clients/example-ui/src/hooks/use-chat-history.tsx Updated chat history hook to manage single chat history
clients/example-ui/src/hooks/use-daydreams.tsx Modified user ID handling and logging
clients/example-ui/src/hooks/use-orchestrators.tsx New hook for orchestrator management
clients/example-ui/src/hooks/use-single-chat-history.tsx Updated single chat history hook for stricter parameters
clients/example-ui/src/index.css Comprehensive styling updates with new color themes
clients/example-ui/src/routeTree.gen.ts Added new route for My Agents
clients/example-ui/src/routes/__root.tsx Reorganized component structure and theme provider
clients/example-ui/src/routes/chats/$chatId.tsx Restructured chat route definition with parameter validation
clients/example-ui/src/routes/chats/index.tsx Updated chat route component to use ChatUI
clients/example-ui/src/routes/index.lazy.tsx Modified index route to render ChatUI component
clients/example-ui/src/routes/my-agents/index.tsx New route for managing orchestrators
clients/example-ui/src/store/use-app-store.ts New Zustand store for global state management
clients/example-ui/tsconfig.json Added JSX and types compiler options

Sequence Diagram

sequenceDiagram
    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
Loading

Possibly related PRs

Poem

🐰 Hop, hop, routing we go!
Zustand state, a rabbit's glow
Messages dance, themes so bright
Our UI takes a quantum flight
Code hops free, no more delay! 🚀


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd418c0 and 5307492.

📒 Files selected for processing (1)
  • clients/example-ui/src/components/chat-ui.tsx (2 hunks)
🔇 Additional comments (1)
clients/example-ui/src/components/chat-ui.tsx (1)

20-20: Verify the impact of removing the chatId prop.

The component no longer accepts a chatId prop and relies on global state management instead. While this simplifies the component's interface, it makes the component more tightly coupled to the global state.

Consider whether this coupling is intentional and aligns with the application's architecture. If the component needs to be reusable in different contexts, prop-based configuration might be more flexible.


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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Cheelax Cheelax changed the title Refacto room Orchestrator manager in client-example Jan 29, 2025
@Cheelax Cheelax marked this pull request as ready for review January 29, 2025 17:04
@Cheelax Cheelax changed the title Orchestrator manager in client-example feat: Orchestrator manager in client-example Jan 29, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 and console.error usage look sound. Consider also verifying if data 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.
The refreshHistory function resets loading & error states but does not call fetchChatHistories again. If you want the button or action to truly refresh data, invoke fetchChatHistories within refreshHistory.

 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 styles
clients/example-ui/src/components/chat-ui.tsx (1)

95-101: Progress feedback.
Good use of a spinner to handle the loading state. Keep watch on potential naming conflicts with isLoading vs. loading from the hook.

examples/example-server.ts (7)

34-37: Optional parameters enhance flexibility.

Allowing an optional params object to configure model and temperature is a smart way to make createDaydreamsAgent 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 if temperature or model are valid. Consider enforcing numeric bounds or an enum of permissible models to avoid unexpected runtime issues.


509-569: Potentially unify logic with createDaydreamsAgent.

createOrchestratorFromDb largely duplicates the initialization steps in createDaydreamsAgent. 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 to MyAgentsPage 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 for initialMessage.

Using any type for initialMessage 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 cached userIdRef.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 unused userId from dependency array.

The userId is included in the useCallback 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:

  1. Add timeout for fetch request
  2. 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:

  1. Add specific error handling for different HTTP status codes
  2. Remove or conditionally enable console logs for production
  3. 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:

  1. Replace any with a specific type for Message.data
  2. 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 readability

Using 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 duplication

All 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

📥 Commits

Reviewing files that changed from the base of the PR and between a7bb540 and c8ae617.

⛔ 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 the generateUserId 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.
Adding orchestratorId helps track the orchestrator for each chat item, aligning well with the store references.


15-16: Great approach to asynchronous state.
Having both loading and error 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.
Renaming message to input is sensible. Props in sibling components should remain consistent if referencing the same data.


33-35: Use of route param chatId.
chatId is only used for logging purposes. Double-check that you truly intend to rely solely on currentOrchestratorId for chat matching instead of chatId.


52-55: Scrolling to latest messages.
Scrolling logic is correct; trigger it once allMessages 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 when currentOrchestratorId 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 the userId 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 with ChatList 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 suggestion

Remove unused RouteComponent function.

The RouteComponent function is defined but never used since the route directly uses ChatUI 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 and temperature properties to OrchestratorChat 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 compliance

The 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.py

Length of output: 1087

Comment on lines 4 to 7
type: "input" | "output" | "system" | "ERROR" | "OTHER" | "WELCOME" | "INFO";
message?: string;
error?: string;
timestamp?: number;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Comment on lines 41 to 49
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]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Comment on lines +148 to +177
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);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Suggested change
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";
Copy link
Contributor

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:

  1. Wrong import path
  2. Incorrect component naming
  3. 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'
Copy link
Contributor

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: Change import ChatUI from "@/components/chat-list" to import 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.

  1. The ChatUI import path (@/components/chat-ui) differs from the previous file (@/components/chat-list)
  2. 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

Comment on lines +62 to +92
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'
);
},

Copy link
Contributor

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.

Suggested change
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'
);
},

Comment on lines +218 to +226
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;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Suggested change
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}`);
}

Comment on lines 286 to 295
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;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Suggested change
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}`);
}
}

Comment on lines 287 to 293
const existingOrchestrator = await this.mongoDb.getOrchestratorById(
new ObjectId(orchestratorId)
);

if (!existingOrchestrator) {
orchestratorId = await this.mongoDb.createOrchestrator(userId);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Comment on lines 213 to 223
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 using any 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 using messages 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
Using useState 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

📥 Commits

Reviewing files that changed from the base of the PR and between c8ae617 and 8c83f5c.

📒 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
The toggleDebug 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 for EXCLUDED_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

Comment on lines +250 to +276
// 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()));
}
Copy link
Contributor

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;
}

Comment on lines +504 to +564
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;
}
Copy link
Contributor

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}`);
        },
    });
}

Comment on lines +144 to +151
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);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Comment on lines +5 to +17
export interface Message {
type: string;
message?: string;
error?: string;
orchestratorId?: string;
timestamp?: number;
messageType?: string;
data?: any;
orchestrators?: Array<{
id: string;
name: string;
}>;
}
Copy link
Contributor

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.

Comment on lines +3 to +7
interface CollapsibleJsonProps {
data: any;
level?: number;
isRoot?: boolean;
}
Copy link
Contributor

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.

Comment on lines +3 to +5
interface DebugPanelProps {
state: Record<string, any>;
}
Copy link
Contributor

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.

Comment on lines 94 to 97
{msg.type !== "input" && msg.type !== "output" && (
<div className="text-xs font-semibold mb-2 opacity-70">
{msg.type}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Suggested change
{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>

Comment on lines +40 to +51
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()
});
}
Copy link
Contributor

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.

Suggested change
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()
});
}

Comment on lines +84 to +97
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(),
});
}
Copy link
Contributor

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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.
If orchestratorId 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 and 0.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 theming

The 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 logic

The 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 system

The 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 styles

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between 86f2844 and a6356bd.

📒 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.tsx

Length 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 type OrchestratorData prevents accidental misuse of the collection. Ensure connect() is called before using this property to avoid runtime errors.


279-279: Clean approach returning OrchestratorData.
Returning Promise<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.
Importing OrchestratorData directly from "./mongo-db" ensures consistent usage across the codebase.


278-278: Validate orchestratorId before .toString().
If orchestratorId is undefined 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 improvements

The MessageType interface now uses consistent uppercase literals and includes timestamp support, improving type safety.


15-20: LGTM! Smooth scroll behavior implementation

Good use of useRef and useEffect for implementing smooth scroll behavior when new messages arrive.


91-91: Avoid using array indices as keys

Using 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 3

Length of output: 3564

Comment on lines +19 to +50
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]);
Copy link
Contributor

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.

  1. Remove or guard console.log statements
  2. Consider caching userId
  3. 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.

Comment on lines +57 to +87
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]);
Copy link
Contributor

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.

Suggested change
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]);

Comment on lines +298 to +307
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);
Copy link
Contributor

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.

Comment on lines +294 to +307
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);
}
Copy link
Contributor

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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:

  1. Ensures type safety by mapping all possible message types
  2. Eliminates the need for a default case
  3. Makes the styling more maintainable and centralized

64-94: Consider improving message identification and time formatting.

  1. Using array indices as keys could cause issues with React's reconciliation if messages are reordered or deleted.
  2. Time formatting could be inconsistent across different locales.

Consider:

  1. 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}>
  1. 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 or tailwind-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

📥 Commits

Reviewing files that changed from the base of the PR and between a6356bd and 6d3ecd5.

📒 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.

Comment on lines 180 to 221
@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;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6d3ecd5 and cd418c0.

📒 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 suggestion

Optimize 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 suggestion

Enhance 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 issue

Limit debug information exposure.

Passing the entire store state to the DebugPanel could potentially expose sensitive information in production.

Consider:

  1. Limiting debug functionality to development environment
  2. 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));
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant