mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-07-04 13:21:17 +00:00
🩹 fix: Address codex/copilot review on API Keys tab
- Exclude `userProvideURL` from the eligibility predicate. A user-provided baseURL alone does not imply a user key, but the tab would still prompt for one via `SetKeyDialog`'s required-fields validation. - Extract `isUserProvidedEndpointConfig` into a shared util so the predicate cannot drift between `Settings.tsx` (tab visibility) and `APIKeys.tsx` (row listing). - Drop the `endpoint as EModelEndpoint` cast in `APIKeyRow`. `SetKeyDialog` already accepts `EModelEndpoint | string`; the cast was unsafe for custom endpoint names. - Add a regression test pinning the baseURL-only exclusion.
This commit is contained in:
parent
13042266d6
commit
511b6307f7
5 changed files with 36 additions and 25 deletions
|
|
@ -101,6 +101,16 @@ describe('Settings', () => {
|
|||
expect(screen.getByText('com_nav_setting_api_keys')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides the API Keys tab for endpoints with a user-provided baseURL but a fixed API key', () => {
|
||||
mockUseGetEndpointsQuery.mockReturnValue({
|
||||
data: { LiteLLM: { userProvide: false, userProvideURL: true, order: 0 } },
|
||||
});
|
||||
|
||||
renderSettings();
|
||||
|
||||
expect(screen.queryByText('com_nav_setting_api_keys')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('resets the active tab when loaded config disables About', async () => {
|
||||
const user = userEvent.setup();
|
||||
const { rerender } = renderSettings();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
APIKeys,
|
||||
About,
|
||||
} from './SettingsTabs';
|
||||
import { isUserProvidedEndpointConfig } from './SettingsTabs/APIKeys/utils';
|
||||
import usePersonalizationAccess from '~/hooks/usePersonalizationAccess';
|
||||
import { useLocalize, TranslationKeys } from '~/hooks';
|
||||
import { useGetEndpointsQuery, useGetStartupConfig } from '~/data-provider';
|
||||
|
|
@ -45,18 +46,7 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
}
|
||||
const values = Object.values(endpointsConfig);
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const config = values[i];
|
||||
if (!config) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
config.userProvide ||
|
||||
config.userProvideURL ||
|
||||
config.userProvideAccessKeyId ||
|
||||
config.userProvideSecretAccessKey ||
|
||||
config.userProvideSessionToken ||
|
||||
config.userProvideBearerToken
|
||||
) {
|
||||
if (isUserProvidedEndpointConfig(values[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import { Button } from '@librechat/client';
|
||||
import { alternateName, getEndpointField } from 'librechat-data-provider';
|
||||
import type { EModelEndpoint, TEndpointsConfig } from 'librechat-data-provider';
|
||||
import type { TEndpointsConfig } from 'librechat-data-provider';
|
||||
import { SetKeyDialog } from '~/components/Input/SetKeyDialog';
|
||||
import { icons } from '~/hooks/Endpoint/Icons';
|
||||
import { useUserKey, useLocalize } from '~/hooks';
|
||||
|
|
@ -62,7 +62,7 @@ const APIKeyRow = ({ endpoint, endpointsConfig }: APIKeyRowProps) => {
|
|||
<SetKeyDialog
|
||||
open={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
endpoint={endpoint as EModelEndpoint}
|
||||
endpoint={endpoint}
|
||||
endpointType={endpointType}
|
||||
userProvideURL={getEndpointField(endpointsConfig, endpoint, 'userProvideURL')}
|
||||
userProvideAccessKeyId={getEndpointField(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { useGetEndpointsQuery } from '~/data-provider';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { isUserProvidedEndpointConfig } from './utils';
|
||||
import APIKeyRow from './APIKeyRow';
|
||||
|
||||
function APIKeys() {
|
||||
|
|
@ -15,17 +16,7 @@ function APIKeys() {
|
|||
const result: string[] = [];
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const [endpoint, config] = entries[i];
|
||||
if (!config) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
config.userProvide ||
|
||||
config.userProvideURL ||
|
||||
config.userProvideAccessKeyId ||
|
||||
config.userProvideSecretAccessKey ||
|
||||
config.userProvideSessionToken ||
|
||||
config.userProvideBearerToken
|
||||
) {
|
||||
if (isUserProvidedEndpointConfig(config)) {
|
||||
result.push(endpoint);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
20
client/src/components/Nav/SettingsTabs/APIKeys/utils.ts
Normal file
20
client/src/components/Nav/SettingsTabs/APIKeys/utils.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import type { TConfig } from 'librechat-data-provider';
|
||||
|
||||
/**
|
||||
* Whether an endpoint config requires a user-provided credential
|
||||
* (API key or any Bedrock credential field) — i.e. should appear in
|
||||
* the API Keys settings tab. `userProvideURL` is intentionally excluded
|
||||
* since a user-provided base URL alone does not imply a user key.
|
||||
*/
|
||||
export const isUserProvidedEndpointConfig = (config: TConfig | null | undefined): boolean => {
|
||||
if (!config) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
!!config.userProvide ||
|
||||
!!config.userProvideAccessKeyId ||
|
||||
!!config.userProvideSecretAccessKey ||
|
||||
!!config.userProvideSessionToken ||
|
||||
!!config.userProvideBearerToken
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue