mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-07 11:08:52 +01:00
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:
parent
8a1968b2f8
commit
583e978a82
90 changed files with 1613 additions and 784 deletions
|
|
@ -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} />,
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue