feat(Google): Support all Text/Chat Models, Response streaming, PaLM -> Google 🤖 (#1316)

* feat: update PaLM icons

* feat: add additional google models

* POC: formatting inputs for Vertex AI streaming

* refactor: move endpoints services outside of /routes dir to /services/Endpoints

* refactor: shorten schemas import

* refactor: rename PALM to GOOGLE

* feat: make Google editable endpoint

* feat: reusable Ask and Edit controllers based off Anthropic

* chore: organize imports/logic

* fix(parseConvo): include examples in googleSchema

* fix: google only allows odd number of messages to be sent

* fix: pass proxy to AnthropicClient

* refactor: change `google` altName to `Google`

* refactor: update getModelMaxTokens and related functions to handle maxTokensMap with nested endpoint model key/values

* refactor: google Icon and response sender changes (Codey and Google logo instead of PaLM in all cases)

* feat: google support for maxTokensMap

* feat: google updated endpoints with Ask/Edit controllers, buildOptions, and initializeClient

* feat(GoogleClient): now builds prompt for text models and supports real streaming from Vertex AI through langchain

* chore(GoogleClient): remove comments, left before for reference in git history

* docs: update google instructions (WIP)

* docs(apis_and_tokens.md): add images to google instructions

* docs: remove typo apis_and_tokens.md

* Update apis_and_tokens.md

* feat(Google): use default settings map, fully support context for both text and chat models, fully support examples for chat models

* chore: update more PaLM references to Google

* chore: move playwright out of workflows to avoid failing tests
This commit is contained in:
Danny Avila 2023-12-10 14:54:13 -05:00 committed by GitHub
parent 8a1968b2f8
commit 583e978a82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
90 changed files with 1613 additions and 784 deletions

View file

@ -1,11 +1,18 @@
import { EModelEndpoint } from 'librechat-data-provider';
import { Plugin, GPTIcon, AnthropicIcon, AzureMinimalIcon } from '~/components/svg';
import {
Plugin,
GPTIcon,
AnthropicIcon,
AzureMinimalIcon,
PaLMIcon,
CodeyIcon,
} from '~/components/svg';
import { useAuthContext } from '~/hooks/AuthContext';
import { IconProps } from '~/common';
import { cn } from '~/utils';
const Icon: React.FC<IconProps> = (props) => {
const { size = 30, isCreatedByUser, button, model = true, endpoint, error, jailbreak } = props;
const { size = 30, isCreatedByUser, button, model = '', endpoint, error, jailbreak } = props;
const { user } = useAuthContext();
@ -52,8 +59,12 @@ const Icon: React.FC<IconProps> = (props) => {
name: 'Plugins',
},
[EModelEndpoint.google]: {
icon: <img src="/assets/google-palm.svg" alt="Palm Icon" />,
name: 'PaLM2',
icon: model?.includes('code') ? (
<CodeyIcon size={size * 0.75} />
) : (
<PaLMIcon size={size * 0.7} />
),
name: model?.includes('code') ? 'Codey' : 'PaLM2',
},
[EModelEndpoint.anthropic]: {
icon: <AnthropicIcon size={size * 0.5555555555555556} />,

View file

@ -5,7 +5,7 @@ import {
LightningIcon,
PluginMinimalIcon,
BingAIMinimalIcon,
PaLMinimalIcon,
GoogleMinimalIcon,
AnthropicIcon,
} from '~/components/svg';
import { cn } from '~/utils';
@ -27,7 +27,7 @@ const MinimalIcon: React.FC<IconProps> = (props) => {
},
[EModelEndpoint.openAI]: { icon: <OpenAIMinimalIcon />, name: props.chatGptLabel || 'ChatGPT' },
[EModelEndpoint.gptPlugins]: { icon: <PluginMinimalIcon />, name: 'Plugins' },
[EModelEndpoint.google]: { icon: <PaLMinimalIcon />, name: props.modelLabel || 'PaLM2' },
[EModelEndpoint.google]: { icon: <GoogleMinimalIcon />, name: props.modelLabel || 'Google' },
[EModelEndpoint.anthropic]: {
icon: <AnthropicIcon className="icon-md shrink-0 dark:text-white" />,
name: props.modelLabel || 'Claude',

View file

@ -1,5 +1,6 @@
import React from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import { EModelEndpoint, endpointSettings } from 'librechat-data-provider';
import type { TModelSelectProps } from '~/common';
import { ESide } from '~/common';
import {
@ -31,7 +32,8 @@ export default function Settings({ conversation, setOption, models, readonly }:
const setTopK = setOption('topK');
const setMaxOutputTokens = setOption('maxOutputTokens');
const codeChat = model?.startsWith('codechat-');
const isTextModel = !model?.includes('chat') && /code|text/.test(model ?? '');
const google = endpointSettings[EModelEndpoint.google];
return (
<div className="grid grid-cols-5 gap-6">
@ -46,45 +48,41 @@ export default function Settings({ conversation, setOption, models, readonly }:
containerClassName="flex w-full resize-none"
/>
</div>
{!codeChat && (
<>
<div className="grid w-full items-center gap-2">
<Label htmlFor="modelLabel" className="text-left text-sm font-medium">
{localize('com_endpoint_custom_name')}{' '}
<small className="opacity-40">({localize('com_endpoint_default_blank')})</small>
</Label>
<Input
id="modelLabel"
disabled={readonly}
value={modelLabel || ''}
onChange={(e) => setModelLabel(e.target.value ?? null)}
placeholder={localize('com_endpoint_google_custom_name_placeholder')}
className={cn(
defaultTextProps,
'flex h-10 max-h-10 w-full resize-none px-3 py-2',
removeFocusOutlines,
)}
/>
</div>
<div className="grid w-full items-center gap-2">
<Label htmlFor="promptPrefix" className="text-left text-sm font-medium">
{localize('com_endpoint_prompt_prefix')}{' '}
<small className="opacity-40">({localize('com_endpoint_default_blank')})</small>
</Label>
<TextareaAutosize
id="promptPrefix"
disabled={readonly}
value={promptPrefix || ''}
onChange={(e) => setPromptPrefix(e.target.value ?? null)}
placeholder={localize('com_endpoint_prompt_prefix_placeholder')}
className={cn(
defaultTextProps,
'flex max-h-[138px] min-h-[100px] w-full resize-none px-3 py-2 ',
)}
/>
</div>
</>
)}
<div className="grid w-full items-center gap-2">
<Label htmlFor="modelLabel" className="text-left text-sm font-medium">
{localize('com_endpoint_custom_name')}{' '}
<small className="opacity-40">({localize('com_endpoint_default_blank')})</small>
</Label>
<Input
id="modelLabel"
disabled={readonly}
value={modelLabel || ''}
onChange={(e) => setModelLabel(e.target.value ?? null)}
placeholder={localize('com_endpoint_google_custom_name_placeholder')}
className={cn(
defaultTextProps,
'flex h-10 max-h-10 w-full resize-none px-3 py-2',
removeFocusOutlines,
)}
/>
</div>
<div className="grid w-full items-center gap-2">
<Label htmlFor="promptPrefix" className="text-left text-sm font-medium">
{localize('com_endpoint_prompt_prefix')}{' '}
<small className="opacity-40">({localize('com_endpoint_default_blank')})</small>
</Label>
<TextareaAutosize
id="promptPrefix"
disabled={readonly}
value={promptPrefix || ''}
onChange={(e) => setPromptPrefix(e.target.value ?? null)}
placeholder={localize('com_endpoint_prompt_prefix_placeholder')}
className={cn(
defaultTextProps,
'flex max-h-[138px] min-h-[100px] w-full resize-none px-3 py-2 ',
)}
/>
</div>
</div>
<div className="col-span-5 flex flex-col items-center justify-start gap-6 px-3 sm:col-span-2">
<HoverCard openDelay={300}>
@ -92,16 +90,18 @@ export default function Settings({ conversation, setOption, models, readonly }:
<div className="flex justify-between">
<Label htmlFor="temp-int" className="text-left text-sm font-medium">
{localize('com_endpoint_temperature')}{' '}
<small className="opacity-40">({localize('com_endpoint_default')}: 0.2)</small>
<small className="opacity-40">
({localize('com_endpoint_default')}: {google.temperature.default})
</small>
</Label>
<InputNumber
id="temp-int"
disabled={readonly}
value={temperature}
onChange={(value) => setTemperature(value ?? 0.2)}
max={1}
min={0}
step={0.01}
onChange={(value) => setTemperature(value ?? google.temperature.default)}
max={google.temperature.max}
min={google.temperature.min}
step={google.temperature.step}
controls={false}
className={cn(
defaultTextProps,
@ -114,18 +114,18 @@ export default function Settings({ conversation, setOption, models, readonly }:
</div>
<Slider
disabled={readonly}
value={[temperature ?? 0.2]}
value={[temperature ?? google.temperature.default]}
onValueChange={(value) => setTemperature(value[0])}
doubleClickHandler={() => setTemperature(0.2)}
max={1}
min={0}
step={0.01}
doubleClickHandler={() => setTemperature(google.temperature.default)}
max={google.temperature.max}
min={google.temperature.min}
step={google.temperature.step}
className="flex h-4 w-full"
/>
</HoverCardTrigger>
<OptionHover endpoint={conversation?.endpoint ?? ''} type="temp" side={ESide.Left} />
</HoverCard>
{!codeChat && (
{!isTextModel && (
<>
<HoverCard openDelay={300}>
<HoverCardTrigger className="grid w-full items-center gap-2">
@ -133,17 +133,17 @@ export default function Settings({ conversation, setOption, models, readonly }:
<Label htmlFor="top-p-int" className="text-left text-sm font-medium">
{localize('com_endpoint_top_p')}{' '}
<small className="opacity-40">
({localize('com_endpoint_default_with_num', '0.95')})
({localize('com_endpoint_default_with_num', google.topP.default + '')})
</small>
</Label>
<InputNumber
id="top-p-int"
disabled={readonly}
value={topP}
onChange={(value) => setTopP(value ?? '0.95')}
max={1}
min={0}
step={0.01}
onChange={(value) => setTopP(value ?? google.topP.default)}
max={google.topP.max}
min={google.topP.min}
step={google.topP.step}
controls={false}
className={cn(
defaultTextProps,
@ -156,12 +156,12 @@ export default function Settings({ conversation, setOption, models, readonly }:
</div>
<Slider
disabled={readonly}
value={[topP ?? 0.95]}
value={[topP ?? google.topP.default]}
onValueChange={(value) => setTopP(value[0])}
doubleClickHandler={() => setTopP(0.95)}
max={1}
min={0}
step={0.01}
doubleClickHandler={() => setTopP(google.topP.default)}
max={google.topP.max}
min={google.topP.min}
step={google.topP.step}
className="flex h-4 w-full"
/>
</HoverCardTrigger>
@ -174,17 +174,17 @@ export default function Settings({ conversation, setOption, models, readonly }:
<Label htmlFor="top-k-int" className="text-left text-sm font-medium">
{localize('com_endpoint_top_k')}{' '}
<small className="opacity-40">
({localize('com_endpoint_default_with_num', '40')})
({localize('com_endpoint_default_with_num', google.topK.default + '')})
</small>
</Label>
<InputNumber
id="top-k-int"
disabled={readonly}
value={topK}
onChange={(value) => setTopK(value ?? 40)}
max={40}
min={1}
step={0.01}
onChange={(value) => setTopK(value ?? google.topK.default)}
max={google.topK.max}
min={google.topK.min}
step={google.topK.step}
controls={false}
className={cn(
defaultTextProps,
@ -197,12 +197,12 @@ export default function Settings({ conversation, setOption, models, readonly }:
</div>
<Slider
disabled={readonly}
value={[topK ?? 40]}
value={[topK ?? google.topK.default]}
onValueChange={(value) => setTopK(value[0])}
doubleClickHandler={() => setTopK(40)}
max={40}
min={1}
step={0.01}
doubleClickHandler={() => setTopK(google.topK.default)}
max={google.topK.max}
min={google.topK.min}
step={google.topK.step}
className="flex h-4 w-full"
/>
</HoverCardTrigger>
@ -216,17 +216,17 @@ export default function Settings({ conversation, setOption, models, readonly }:
<Label htmlFor="max-tokens-int" className="text-left text-sm font-medium">
{localize('com_endpoint_max_output_tokens')}{' '}
<small className="opacity-40">
({localize('com_endpoint_default_with_num', '1024')})
({localize('com_endpoint_default_with_num', google.maxOutputTokens.default + '')})
</small>
</Label>
<InputNumber
id="max-tokens-int"
disabled={readonly}
value={maxOutputTokens}
onChange={(value) => setMaxOutputTokens(value ?? 1024)}
max={1024}
min={1}
step={1}
onChange={(value) => setMaxOutputTokens(value ?? google.maxOutputTokens.default)}
max={google.maxOutputTokens.max}
min={google.maxOutputTokens.min}
step={google.maxOutputTokens.step}
controls={false}
className={cn(
defaultTextProps,
@ -239,12 +239,12 @@ export default function Settings({ conversation, setOption, models, readonly }:
</div>
<Slider
disabled={readonly}
value={[maxOutputTokens ?? 1024]}
value={[maxOutputTokens ?? google.maxOutputTokens.default]}
onValueChange={(value) => setMaxOutputTokens(value[0])}
doubleClickHandler={() => setMaxOutputTokens(1024)}
max={1024}
min={1}
step={1}
doubleClickHandler={() => setMaxOutputTokens(google.maxOutputTokens.default)}
max={google.maxOutputTokens.max}
min={google.maxOutputTokens.min}
step={google.maxOutputTokens.step}
className="flex h-4 w-full"
/>
</HoverCardTrigger>

View file

@ -16,7 +16,7 @@ export default function GoogleView({ conversation, models, isPreset = false }) {
const { showExamples, isCodeChat } = optionSettings;
return showExamples && !isCodeChat ? (
<Examples
examples={examples ?? []}
examples={examples ?? [{ input: { content: '' }, output: { content: '' } }]}
setExample={setExample}
addExample={addExample}
removeExample={removeExample}