Show when there's server-side key support

This commit is contained in:
Enrico Ros
2023-10-10 01:45:11 -07:00
parent 6ee231d271
commit dcd7f65223
11 changed files with 43 additions and 37 deletions
+27 -22
View File
@@ -1,7 +1,7 @@
import * as React from 'react';
import { shallow } from 'zustand/shallow';
import { Box, Button, IconButton, ListItemDecorator, MenuItem, Option, Select, Typography } from '@mui/joy';
import { Badge, Box, Button, IconButton, ListItemDecorator, MenuItem, Option, Select, Typography } from '@mui/joy';
import AddIcon from '@mui/icons-material/Add';
import CloudDoneOutlinedIcon from '@mui/icons-material/CloudDoneOutlined';
import CloudOutlinedIcon from '@mui/icons-material/CloudOutlined';
@@ -10,8 +10,8 @@ import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import { DModelSourceId, useModelsStore } from '~/modules/llms/store-llms';
import { IModelVendor, ModelVendorId } from '~/modules/llms/vendors/IModelVendor';
import { ModelVendorOpenAI } from '~/modules/llms/vendors/openai/openai.vendor';
import { createModelSourceForVendor, findAllVendors, findVendorById } from '~/modules/llms/vendors/vendor.registry';
import { hasServerKeyOpenAI } from '~/modules/llms/vendors/openai/openai.vendor';
import { CloseableMenu } from '~/common/components/CloseableMenu';
import { ConfirmationModal } from '~/common/components/ConfirmationModal';
@@ -19,14 +19,16 @@ import { hideOnDesktop, hideOnMobile } from '~/common/theme';
function locationIcon(vendor?: IModelVendor | null) {
if (vendor && vendor.id === 'openai' && hasServerKeyOpenAI)
if (vendor && vendor.id === 'openai' && ModelVendorOpenAI.hasServerKey)
return <CloudDoneOutlinedIcon />;
return !vendor ? null : vendor.location === 'local' ? <ComputerIcon /> : <CloudOutlinedIcon />;
}
function vendorIcon(vendor?: IModelVendor | null) {
function vendorIcon(vendor: IModelVendor | null, greenMark: boolean) {
const Icon = !vendor ? null : vendor.Icon;
return Icon ? <Icon /> : null;
return (greenMark && Icon)
? <Badge color='primary' size='sm' badgeContent=''><Icon /></Badge>
: Icon ? <Icon /> : null;
}
@@ -73,23 +75,26 @@ export function ModelsSourceSelector(props: {
// vendor list items
const vendorItems = React.useMemo(() => findAllVendors().filter(v => !!v.instanceLimit).map(vendor => {
const sourceCount = modelSources.filter(source => source.vId === vendor.id).length;
const enabled = vendor.instanceLimit > sourceCount;
return {
vendor,
enabled,
sourceCount,
component: (
<MenuItem key={vendor.id} disabled={!enabled} onClick={() => handleAddSourceFromVendor(vendor.id)}>
<ListItemDecorator>
{vendorIcon(vendor)}
</ListItemDecorator>
{vendor.name}{/*{sourceCount > 0 && ` (added)`}*/}
</MenuItem>
),
};
}), [handleAddSourceFromVendor, modelSources]);
const vendorItems = React.useMemo(() => findAllVendors()
.filter(v => !!v.instanceLimit)
.map(vendor => {
const sourceCount = modelSources.filter(source => source.vId === vendor.id).length;
const enabled = vendor.instanceLimit > sourceCount;
return {
vendor,
enabled,
sourceCount,
component: (
<MenuItem key={vendor.id} disabled={!enabled} onClick={() => handleAddSourceFromVendor(vendor.id)}>
<ListItemDecorator>
{vendorIcon(vendor, !!vendor.hasServerKey)}
</ListItemDecorator>
{vendor.name}{/*{sourceCount > 0 && ` (added)`}*/}
</MenuItem>
),
};
},
), [handleAddSourceFromVendor, modelSources]);
// source items
+1
View File
@@ -13,6 +13,7 @@ export interface IModelVendor<TSourceSetup = unknown, TLLMOptions = unknown, TAc
readonly rank: number;
readonly location: 'local' | 'cloud';
readonly instanceLimit: number;
readonly hasServerKey?: boolean;
// components
readonly Icon: React.ComponentType;
@@ -13,7 +13,7 @@ import { settingsGap } from '~/common/theme';
import { DModelSourceId, useModelsStore, useSourceSetup } from '../../store-llms';
import { modelDescriptionToDLLM } from '../openai/OpenAISourceSetup';
import { hasServerKeyAnthropic, isValidAnthropicApiKey, ModelVendorAnthropic } from './anthropic.vendor';
import { isValidAnthropicApiKey, ModelVendorAnthropic } from './anthropic.vendor';
export function AnthropicSourceSetup(props: { sourceId: DModelSourceId }) {
@@ -25,7 +25,7 @@ export function AnthropicSourceSetup(props: { sourceId: DModelSourceId }) {
// derived state
const { anthropicKey } = access;
const needsUserKey = !hasServerKeyAnthropic;
const needsUserKey = !ModelVendorAnthropic.hasServerKey;
const keyValid = isValidAnthropicApiKey(anthropicKey);
const keyError = (/*needsUserKey ||*/ !!anthropicKey) && !keyValid;
const shallFetchSucceed = anthropicKey ? keyValid : !needsUserKey;
+1 -1
View File
@@ -13,7 +13,6 @@ import { AnthropicSourceSetup } from './AnthropicSourceSetup';
// special symbols
export const hasServerKeyAnthropic = !!process.env.HAS_SERVER_KEY_ANTHROPIC;
export const isValidAnthropicApiKey = (apiKey?: string) => !!apiKey && apiKey.startsWith('sk-') && apiKey.length > 40;
export interface SourceSetupAnthropic {
@@ -27,6 +26,7 @@ export const ModelVendorAnthropic: IModelVendor<SourceSetupAnthropic, LLMOptions
rank: 13,
location: 'cloud',
instanceLimit: 1,
hasServerKey: !!process.env.HAS_SERVER_KEY_ANTHROPIC,
// components
Icon: AnthropicIcon,
+2 -2
View File
@@ -13,7 +13,7 @@ import { settingsGap } from '~/common/theme';
import { DModelSourceId, useModelsStore, useSourceSetup } from '../../store-llms';
import { modelDescriptionToDLLM } from '../openai/OpenAISourceSetup';
import { hasServerKeyAzure, isValidAzureApiKey, ModelVendorAzure } from './azure.vendor';
import { isValidAzureApiKey, ModelVendorAzure } from './azure.vendor';
export function AzureSourceSetup(props: { sourceId: DModelSourceId }) {
@@ -25,7 +25,7 @@ export function AzureSourceSetup(props: { sourceId: DModelSourceId }) {
// derived state
const { oaiKey: azureKey, oaiHost: azureEndpoint } = access;
const needsUserKey = !hasServerKeyAzure;
const needsUserKey = !ModelVendorAzure.hasServerKey;
const keyValid = isValidAzureApiKey(azureKey);
const keyError = (/*needsUserKey ||*/ !!azureKey) && !keyValid;
const hostValid = !!asValidURL(azureEndpoint);
+2 -2
View File
@@ -11,7 +11,6 @@ import { AzureSourceSetup } from './AzureSourceSetup';
// special symbols
export const hasServerKeyAzure = !!process.env.HAS_SERVER_KEY_AZURE_OPENAI;
export const isValidAzureApiKey = (apiKey?: string) => !!apiKey && apiKey.length >= 32;
export interface SourceSetupAzure {
@@ -40,7 +39,8 @@ export const ModelVendorAzure: IModelVendor<SourceSetupAzure, LLMOptionsOpenAI,
name: 'Azure',
rank: 14,
location: 'cloud',
instanceLimit: 0,
instanceLimit: 2,
hasServerKey: !!process.env.HAS_SERVER_KEY_AZURE_OPENAI,
// components
Icon: AzureIcon,
+1 -1
View File
@@ -17,7 +17,7 @@ export interface SourceSetupOobabooga {
export const ModelVendorOoobabooga: IModelVendor<SourceSetupOobabooga, LLMOptionsOpenAI, OpenAIAccessSchema> = {
id: 'oobabooga',
name: 'Oobabooga',
rank: 15,
rank: 25,
location: 'local',
instanceLimit: 1,
+2 -2
View File
@@ -14,7 +14,7 @@ import { settingsCol1Width, settingsGap } from '~/common/theme';
import type { ModelDescriptionSchema } from '../../transports/server/server.common';
import { DLLM, DModelSource, DModelSourceId, useModelsStore, useSourceSetup } from '../../store-llms';
import { hasServerKeyOpenAI, isValidOpenAIApiKey, LLMOptionsOpenAI, ModelVendorOpenAI, SourceSetupOpenAI } from './openai.vendor';
import { isValidOpenAIApiKey, LLMOptionsOpenAI, ModelVendorOpenAI, SourceSetupOpenAI } from './openai.vendor';
import { openAIModelToModelDescription } from './openai.data';
@@ -30,7 +30,7 @@ export function OpenAISourceSetup(props: { sourceId: DModelSourceId }) {
// derived state
const { oaiKey, oaiOrg, oaiHost, heliKey, moderationCheck } = access;
const needsUserKey = !hasServerKeyOpenAI;
const needsUserKey = !ModelVendorOpenAI.hasServerKey;
const keyValid = isValidOpenAIApiKey(oaiKey);
const keyError = (/*needsUserKey ||*/ !!oaiKey) && !keyValid;
const shallFetchSucceed = oaiKey ? keyValid : !needsUserKey;
+1 -1
View File
@@ -11,7 +11,6 @@ import { OpenAISourceSetup } from './OpenAISourceSetup';
// special symbols
export const hasServerKeyOpenAI = !!process.env.HAS_SERVER_KEY_OPENAI;
export const isValidOpenAIApiKey = (apiKey?: string) => !!apiKey && apiKey.startsWith('sk-') && apiKey.length > 40;
export interface SourceSetupOpenAI {
@@ -34,6 +33,7 @@ export const ModelVendorOpenAI: IModelVendor<SourceSetupOpenAI, LLMOptionsOpenAI
rank: 10,
location: 'cloud',
instanceLimit: 1,
hasServerKey: !!process.env.HAS_SERVER_KEY_OPENAI,
// components
Icon: OpenAIIcon,
@@ -13,7 +13,7 @@ import { settingsGap } from '~/common/theme';
import { LLMOptionsOpenAI } from '../openai/openai.vendor';
import { DLLM, DModelSource, DModelSourceId, useModelsStore, useSourceSetup } from '../../store-llms';
import { hasServerKeyOpenRouter, isValidOpenRouterKey, ModelVendorOpenRouter, SourceSetupOpenRouter } from './openrouter.vendor';
import { isValidOpenRouterKey, ModelVendorOpenRouter, SourceSetupOpenRouter } from './openrouter.vendor';
export function OpenRouterSourceSetup(props: { sourceId: DModelSourceId }) {
@@ -26,7 +26,7 @@ export function OpenRouterSourceSetup(props: { sourceId: DModelSourceId }) {
// derived state
const { oaiKey } = access;
const needsUserKey = !hasServerKeyOpenRouter;
const needsUserKey = !ModelVendorOpenRouter.hasServerKey;
const keyValid = isValidOpenRouterKey(oaiKey);
const keyError = (/*needsUserKey ||*/ !!oaiKey) && !keyValid;
const shallFetchSucceed = oaiKey ? keyValid : !needsUserKey;
+2 -2
View File
@@ -11,7 +11,6 @@ import { OpenRouterSourceSetup } from './OpenRouterSourceSetup';
// special symbols
export const hasServerKeyOpenRouter = !!process.env.HAS_SERVER_KEY_OPENROUTER;
export const isValidOpenRouterKey = (apiKey?: string) => !!apiKey && apiKey.startsWith('sk-or-') && apiKey.length > 40;
// use OpenAI-compatible host and key
@@ -34,9 +33,10 @@ export interface SourceSetupOpenRouter {
export const ModelVendorOpenRouter: IModelVendor<SourceSetupOpenRouter, LLMOptionsOpenAI, OpenAIAccessSchema> = {
id: 'openrouter',
name: 'OpenRouter',
rank: 25,
rank: 12,
location: 'cloud',
instanceLimit: 1,
hasServerKey: !!process.env.HAS_SERVER_KEY_OPENAI,
// components
Icon: OpenRouterIcon,