📌 feat: Pin Agents and Models in the Sidebar (#10634)

* 🪦 refactor: Remove Legacy Code (#10533)

* 🗑️ chore: Remove unused Legacy Provider clients and related helpers

* Deleted OpenAIClient and GoogleClient files along with their associated tests.
* Removed references to these clients in the clients index file.
* Cleaned up typedefs by removing the OpenAISpecClient export.
* Updated chat controllers to use the OpenAI SDK directly instead of the removed client classes.

* chore/remove-openapi-specs

* 🗑️ chore: Remove unused mergeSort and misc utility functions

* Deleted mergeSort.js and misc.js files as they are no longer needed.
* Removed references to cleanUpPrimaryKeyValue in messages.js and adjusted related logic.
* Updated mongoMeili.ts to eliminate local implementations of removed functions.

* chore: remove legacy endpoints

* chore: remove all plugins endpoint related code

* chore: remove unused prompt handling code and clean up imports

* Deleted handleInputs.js and instructions.js files as they are no longer needed.
* Removed references to these files in the prompts index.js.
* Updated docker-compose.yml to simplify reverse proxy configuration.

* chore: remove unused LightningIcon import from Icons.tsx

* chore: clean up translation.json by removing deprecated and unused keys

* chore: update Jest configuration and remove unused mock file

    * Simplified the setupFiles array in jest.config.js by removing the fetchEventSource mock.
    * Deleted the fetchEventSource.js mock file as it is no longer needed.

* fix: simplify endpoint type check in Landing and ConversationStarters components

    * Updated the endpoint type check to use strict equality for better clarity and performance.
    * Ensured consistency in the handling of the azureOpenAI endpoint across both components.

* chore: remove unused dependencies from package.json and package-lock.json

* chore: remove legacy EditController, associated routes and imports

* chore: update banResponse logic to refine request handling for banned users

* chore: remove unused validateEndpoint middleware and its references

* chore: remove unused 'res' parameter from initializeClient in multiple endpoint files

* chore: remove unused 'isSmallScreen' prop from BookmarkNav and NewChat components; clean up imports in ArchivedChatsTable and useSetIndexOptions hooks; enhance localization in PromptVersions

* chore: remove unused import of Constants and TMessage from MobileNav; retain only necessary QueryKeys import

* chore: remove unused TResPlugin type and related references; clean up imports in types and schemas

* 🪦 refactor: Remove Legacy Code (#10533)

* 🗑️ chore: Remove unused Legacy Provider clients and related helpers

* Deleted OpenAIClient and GoogleClient files along with their associated tests.
* Removed references to these clients in the clients index file.
* Cleaned up typedefs by removing the OpenAISpecClient export.
* Updated chat controllers to use the OpenAI SDK directly instead of the removed client classes.

* chore/remove-openapi-specs

* 🗑️ chore: Remove unused mergeSort and misc utility functions

* Deleted mergeSort.js and misc.js files as they are no longer needed.
* Removed references to cleanUpPrimaryKeyValue in messages.js and adjusted related logic.
* Updated mongoMeili.ts to eliminate local implementations of removed functions.

* chore: remove legacy endpoints

* chore: remove all plugins endpoint related code

* chore: remove unused prompt handling code and clean up imports

* Deleted handleInputs.js and instructions.js files as they are no longer needed.
* Removed references to these files in the prompts index.js.
* Updated docker-compose.yml to simplify reverse proxy configuration.

* chore: remove unused LightningIcon import from Icons.tsx

* chore: clean up translation.json by removing deprecated and unused keys

* chore: update Jest configuration and remove unused mock file

    * Simplified the setupFiles array in jest.config.js by removing the fetchEventSource mock.
    * Deleted the fetchEventSource.js mock file as it is no longer needed.

* fix: simplify endpoint type check in Landing and ConversationStarters components

    * Updated the endpoint type check to use strict equality for better clarity and performance.
    * Ensured consistency in the handling of the azureOpenAI endpoint across both components.

* chore: remove unused dependencies from package.json and package-lock.json

* chore: remove legacy EditController, associated routes and imports

* chore: update banResponse logic to refine request handling for banned users

* chore: remove unused validateEndpoint middleware and its references

* chore: remove unused 'res' parameter from initializeClient in multiple endpoint files

* chore: remove unused 'isSmallScreen' prop from BookmarkNav and NewChat components; clean up imports in ArchivedChatsTable and useSetIndexOptions hooks; enhance localization in PromptVersions

* chore: remove unused import of Constants and TMessage from MobileNav; retain only necessary QueryKeys import

* chore: remove unused TResPlugin type and related references; clean up imports in types and schemas

* 📦 chore: Bump Express.js to v5 (#10671)

* chore: update express to version 5.1.0 in package.json

* chore: update express-rate-limit to version 8.2.1 in package.json and package-lock.json

* fix: Enhance server startup error handling in experimental and index files

* Added error handling for server startup in both experimental.js and index.js to log errors and exit the process if the server fails to start.
* Updated comments in openidStrategy.js to clarify the purpose of the CustomOpenIDStrategy class and its relation to Express version changes.

* chore: Implement rate limiting for all POST routes excluding /speech, required for express v5

* Added middleware to apply IP and user rate limiters to all POST requests, ensuring that the /speech route remains unaffected.
* Enhanced code clarity with comments explaining the new rate limiting logic.

* chore: Enable writable req.query for mongoSanitize compatibility in Express 5

* chore: Ensure req.body exists in multiple middleware and route files for Express 5 compatibility

* 🗣 feat: MCP Status Accessibility Improvements (#10738)

* feat: make MultiSelect highlight same opacity as other focus highlights in app

* feat: add better screenreader announcements for mcp server and variable states

* feat: memoize fullTitle calculation

* 🪨 feat: Add PROXY support for AWS Bedrock endpoints (#8871)

* feat: added PROXY support for AWS Bedrock endpoint

* chore: explicit install of new packages required for bedrock proxy

---------

Co-authored-by: Danny Avila <danny@librechat.ai>

*  feat: Implement Favorites functionality with controllers, hooks, and UI components

*  feat: Refactor Favorites functionality to support new data structure and enhance UI interactions

*  feat: Add endpoint to new conversation for agent favorites

*  feat: Enhance Conversations and Favorites components with expanded functionality and improved UI interactions

*  feat: Remove 'Pinned' label from UI translations for cleaner interface

* feat: clean up comments and improve code readability in favorites and agent components; bump @librechat/data-schemas to 0.0.24

*  feat: Enhance favorites management with validation, update data structure, and improve UI interactions

*  feat: Simplify rendering logic in EndpointModelItem and optimize useEffect dependencies in Conversations component

*  test: Update favorites mock implementation and improve button focus styles in AgentDetail tests

*  feat: Enhance favorites management by adding loading and error states, and refactor related hooks and components

*  feat: Add loading skeletons for favorites while agents are being fetched

*  feat: Improve loading experience in FavoritesList by adding skeleton placeholders for favorites and marketplace

* feat: Optimize cache handling in Conversations and enhance FavoritesList to notify height changes on loading completion

*  feat: Add loading skeleton for SearchBar in Nav component and update agent avatar fallback icon to Feather

* feat: Refactor FavoritesController validation, streamline ModelSelector component, and enhance EndpointModelItem with selection state

* feat: Adjust padding in Conversations and FavoritesList components for improved layout consistency

* feat: Refactor FavoritesController to use model methods for user updates and retrieval

* feat: Enhance Favorites functionality with validation, cleanup, and improved error handling

* tests: Update AgentCard and agent utilities to use Feather icon fallback instead of Bot icon

* refactor: Remove collapsible animation styles from CSS

* feat: Migrate favorites state management from Recoil to Jotai

* fix: Correct type definition in useGetFavoritesQuery and ensure useFavorites is exported

* refactor: Simplify AuthField component by removing TooltipAnchor and directly rendering Label

* fix: Ensure favorites are always an array and update references in FavoritesList

* style: Update Conversation component styles for improved UI consistency

* feat: re-integrate AuthContext to manage agent marketplace visibility based on authentication state

* fix: Improve optimistic updates in favorites mutation handling

* feat: Implement error handling for favorites limit and consolidate marketplace access logic

* fix: package-lock

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
Co-authored-by: Dustin Healy <54083382+dustinhealy@users.noreply.github.com>
Co-authored-by: Arthur Barrett <abarrett@fas.harvard.edu>
This commit is contained in:
Marco Beretta 2025-12-04 20:41:52 +01:00 committed by GitHub
parent 6aa696b74f
commit 9f2490ac46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 1311 additions and 185 deletions

View file

@ -4,11 +4,16 @@ import '@testing-library/jest-dom';
import { getAgentAvatarUrl, renderAgentAvatar, getContactDisplayName } from '../agents';
import type t from 'librechat-data-provider';
// Mock the Bot icon from lucide-react
// Mock the Feather icon from lucide-react
jest.mock('lucide-react', () => ({
Bot: ({ className, strokeWidth, ...props }: any) => (
<svg data-testid="bot-icon" className={className} data-stroke-width={strokeWidth} {...props}>
<title>{/* eslint-disable-line i18next/no-literal-string */}Bot Icon</title>
Feather: ({ className, strokeWidth, ...props }: any) => (
<svg
data-testid="feather-icon"
className={className}
data-stroke-width={strokeWidth}
{...props}
>
<title>{/* eslint-disable-line i18next/no-literal-string */}Feather Icon</title>
</svg>
),
}));
@ -72,7 +77,7 @@ describe('Agent Utilities', () => {
expect(img).toHaveClass('rounded-full', 'object-cover', 'shadow-lg');
});
it('should render Bot icon fallback when no avatar', () => {
it('should render Feather icon fallback when no avatar', () => {
const agent = {
id: '1',
name: 'Test Agent',
@ -80,9 +85,9 @@ describe('Agent Utilities', () => {
render(<div>{renderAgentAvatar(agent)}</div>);
const botIcon = screen.getByTestId('bot-icon');
expect(botIcon).toBeInTheDocument();
expect(botIcon).toHaveAttribute('data-stroke-width', '1.5');
const featherIcon = screen.getByTestId('feather-icon');
expect(featherIcon).toBeInTheDocument();
expect(featherIcon).toHaveAttribute('data-stroke-width', '1.5');
});
it('should apply different size classes', () => {

View file

@ -1,5 +1,6 @@
import React from 'react';
import { Bot } from 'lucide-react';
import React, { useState, useEffect } from 'react';
import { Feather } from 'lucide-react';
import { Skeleton } from '@librechat/client';
import type t from 'librechat-data-provider';
/**
@ -22,6 +23,40 @@ export const getAgentAvatarUrl = (agent: t.Agent | null | undefined): string | n
return null;
};
const LazyAgentAvatar = ({
url,
alt,
imgClass,
}: {
url: string;
alt: string;
imgClass: string;
}) => {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
setIsLoaded(false);
}, [url]);
return (
<>
<img
src={url}
alt={alt}
className={imgClass}
loading="lazy"
onLoad={() => setIsLoaded(true)}
onError={() => setIsLoaded(false)}
style={{
opacity: isLoaded ? 1 : 0,
transition: 'opacity 0.2s ease-in-out',
}}
/>
{!isLoaded && <Skeleton className="absolute inset-0 rounded-full" aria-hidden="true" />}
</>
);
};
/**
* Renders an agent avatar with fallback to Bot icon
* Consistent across all agent displays
@ -29,7 +64,7 @@ export const getAgentAvatarUrl = (agent: t.Agent | null | undefined): string | n
export const renderAgentAvatar = (
agent: t.Agent | null | undefined,
options: {
size?: 'sm' | 'md' | 'lg' | 'xl';
size?: 'icon' | 'sm' | 'md' | 'lg' | 'xl';
className?: string;
showBorder?: boolean;
} = {},
@ -40,6 +75,7 @@ export const renderAgentAvatar = (
// Size mappings for responsive design
const sizeClasses = {
icon: 'h-5 w-5',
sm: 'h-12 w-12 sm:h-14 sm:w-14',
md: 'h-16 w-16 sm:h-20 sm:w-20 md:h-24 md:w-24',
lg: 'h-20 w-20 sm:h-24 sm:w-24 md:h-28 md:w-28',
@ -47,6 +83,7 @@ export const renderAgentAvatar = (
};
const iconSizeClasses = {
icon: 'h-4 w-4',
sm: 'h-6 w-6 sm:h-7 sm:w-7',
md: 'h-6 w-6 sm:h-8 sm:w-8 md:h-10 md:w-10',
lg: 'h-8 w-8 sm:h-10 sm:w-10 md:h-12 md:w-12',
@ -54,6 +91,7 @@ export const renderAgentAvatar = (
};
const placeholderSizeClasses = {
icon: 'h-5 w-5',
sm: 'h-10 w-10 sm:h-12 sm:w-12',
md: 'h-12 w-12 sm:h-16 sm:w-16 md:h-20 md:w-20',
lg: 'h-16 w-16 sm:h-20 sm:w-20 md:h-24 md:w-24',
@ -64,27 +102,21 @@ export const renderAgentAvatar = (
if (avatarUrl) {
return (
<div className={`flex items-center justify-center ${sizeClasses[size]} ${className}`}>
<img
src={avatarUrl}
<div
className={`relative flex items-center justify-center ${sizeClasses[size]} ${className}`}
>
<LazyAgentAvatar
url={avatarUrl}
alt={`${agent?.name || 'Agent'} avatar`}
className={`${sizeClasses[size]} rounded-full object-cover shadow-lg ${borderClasses}`}
loading="lazy"
imgClass={`${sizeClasses[size]} rounded-full object-cover shadow-lg ${borderClasses}`}
/>
</div>
);
}
// Fallback placeholder with Bot icon
return (
<div className={`relative flex items-center justify-center ${sizeClasses[size]} ${className}`}>
{/* Subtle minimalistic placeholder */}
<div className="absolute inset-0 rounded-full border border-border-medium bg-surface-secondary"></div>
<div
className={`relative flex items-center justify-center rounded-full ${placeholderSizeClasses[size]}`}
>
<Bot className={`text-text-primary ${iconSizeClasses[size]}`} strokeWidth={1.5} />
</div>
<Feather className={`text-text-primary ${iconSizeClasses[size]}`} strokeWidth={1.5} />
</div>
);
};