feat(ChatAgent.js): add support for skipping completion mode in ChatAgent

feat(ChatAgent.js): add a check for images when completion is skipped to add to response
feat(askGPTPlugins.js): add skipCompletion option to agentOptions
feat(client): add Switch component to ui components and use for new Agent Settings
chore(package.json): ignore client directory in nodemonConfig
This commit is contained in:
Daniel Avila 2023-06-15 20:53:28 -04:00 committed by Danny Avila
parent 5b1efc48d1
commit d0be2e6f4a
13 changed files with 150 additions and 32 deletions

View file

@ -6,7 +6,7 @@ const {
} = require('@dqbd/tiktoken');
const { fetchEventSource } = require('@waylaidwanderer/fetch-event-source');
const { Agent, ProxyAgent } = require('undici');
// const TextStream = require('../stream');
const TextStream = require('../stream');
const { ChatOpenAI } = require('langchain/chat_models/openai');
const { CallbackManager } = require('langchain/callbacks');
const { HumanChatMessage, AIChatMessage } = require('langchain/schema');
@ -687,13 +687,14 @@ Only respond with your conversational reply to the following User Message:
return { ...responseMessage, ...this.result };
}
// if (!this.agentIsGpt3 && this.result.output) {
// responseMessage.text = this.result.output;
// await this.saveMessageToDatabase(responseMessage, user);
// const textStream = new TextStream(this.result.output);
// await textStream.processTextStream(opts.onProgress);
// return { ...responseMessage, ...this.result };
// }
if (!completionMode && this.agentOptions.skipCompletion && this.result.output) {
responseMessage.text = this.result.output;
this.addImages(this.result.intermediateSteps, responseMessage);
await this.saveMessageToDatabase(responseMessage, user);
const textStream = new TextStream(this.result.output);
await textStream.processTextStream(opts.onProgress);
return { ...responseMessage, ...this.result };
}
if (this.options.debug) {
console.debug('this.result', this.result);
@ -714,6 +715,26 @@ Only respond with your conversational reply to the following User Message:
return { ...responseMessage, ...this.result };
}
addImages(intermediateSteps, responseMessage) {
if (!intermediateSteps || !responseMessage) {
return;
}
intermediateSteps.forEach(step => {
const { observation } = step;
if (!observation || !observation.includes('![')) {
return;
}
if (!responseMessage.text.includes(observation)) {
responseMessage.text += '\n' + observation;
if (this.options.debug) {
console.debug('added image from intermediateSteps');
}
}
});
}
async buildPrompt({ messages, promptPrefix: _promptPrefix, completionMode = false, isChatGptModel = true }) {
if (this.options.debug) {
console.debug('buildPrompt messages', messages);

View file

@ -40,6 +40,7 @@ router.post('/', requireJwtAuth, async (req, res) => {
const agentOptions = req.body?.agentOptions ?? {
agent: 'classic',
skipCompletion: false,
model: 'gpt-3.5-turbo',
temperature: 0,
// top_p: 1,

View file

@ -36,6 +36,7 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-slider": "^1.1.1",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.3",
"@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.28.0",

View file

@ -239,6 +239,7 @@ const EditPresetDialog = ({ open, onOpenChange, preset: _preset, title }) => {
{preset?.endpoint === 'gptPlugins' && showAgentSettings && (
<AgentSettings
agent={preset.agent}
skipCompletion={preset.agentOptions.skipCompletion}
model={preset.agentOptions.model}
endpoint={preset.agentOptions.endpoint}
temperature={preset.agentOptions.temperature}

View file

@ -1,6 +1,7 @@
import { cn } from '~/utils/';
import { useRecoilValue } from 'recoil';
import {
Switch,
SelectDropDown,
Label,
Slider,
@ -21,26 +22,28 @@ function Settings(props) {
const {
readonly,
agent,
skipCompletion,
model,
temperature,
// topP,
// freqP,
// presP,
setOption,
// tools
} = props;
const endpoint = 'gptPlugins';
const endpointsConfig = useRecoilValue(store.endpointsConfig);
const setModel = setOption('model');
const setTemperature = setOption('temperature');
// const setTopP = setOption('top_p');
// const setFreqP = setOption('presence_penalty');
// const setPresP = setOption('frequency_penalty');
const setAgent = setOption('agent');
const setSkipCompletion = setOption('skipCompletion');
const onCheckedChangeAgent = (checked) => {
setAgent(checked ? 'functions' : 'classic');
};
const onCheckedChangeSkip = (checked) => {
setSkipCompletion(checked);
};
// const toolsSelected = tools?.length > 0;
const models = endpointsConfig?.[endpoint]?.['availableModels'] || [];
const agents = endpointsConfig?.[endpoint]?.['availableAgents'] || [];
return (
<div className="max-h-[350px] min-h-[305px] overflow-y-auto">
@ -60,19 +63,31 @@ function Settings(props) {
containerClassName="flex w-full resize-none"
/>
</div>
<div className="grid w-full items-center gap-2">
<SelectDropDown
title="Agent Mode"
value={agent}
setValue={setOption('agent')}
availableValues={agents}
disabled={readonly}
className={cn(
defaultTextProps,
'flex w-full resize-none focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
)}
containerClassName="flex w-full resize-none"
/>
<div className="grid w-full items-center gap-2 grid-cols-2">
<HoverCard openDelay={500}>
<HoverCardTrigger className='w-1/2'>
<label
htmlFor="functions-agent"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-gray-50"
>
<small>Use Functions</small>
</label>
<Switch id="functions-agent" checked={agent === 'functions'} onCheckedChange={onCheckedChangeAgent} disabled={readonly} className="mt-2 ml-4"/>
</HoverCardTrigger>
<OptionHover type="temp" side="right" />
</HoverCard>
<HoverCard openDelay={500}>
<HoverCardTrigger className='w-1/2 ml-[-60px]'>
<label
htmlFor="skip-completion"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-gray-50"
>
<small>Skip Completion</small>
</label>
<Switch id="skip-completion" checked={skipCompletion === true} onCheckedChange={onCheckedChangeSkip} disabled={readonly} className="mt-2 ml-4"/>
</HoverCardTrigger>
<OptionHover type="temp" side="right" />
</HoverCard>
</div>
</div>
<div className="col-span-1 flex flex-col items-center justify-start gap-6">

View file

@ -188,6 +188,7 @@ function PluginsOptions() {
{showAgentSettings ? (
<AgentSettings
agent={agentOptions.agent}
skipCompletion={agentOptions.skipCompletion}
model={agentOptions.model}
endpoint={agentOptions.endpoint}
temperature={agentOptions.temperature}

View file

@ -0,0 +1,27 @@
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from '../../utils';
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-green-600 data-[state=unchecked]:bg-gray-200",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-white shadow-[0_1px_2px_rgba(0,0,0,0.45)] transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }

View file

@ -11,6 +11,7 @@ export * from './Landing';
export * from './ModelSelect';
export * from './Prompt';
export * from './Slider';
export * from './Switch';
export * from './Tabs';
export * from './Templates';
export * from './Textarea';

View file

@ -52,6 +52,7 @@ const cleanupPreset = ({ preset: _preset, endpointsConfig = {} }) => {
} else if (endpoint === 'gptPlugins') {
const agentOptions = _preset?.agentOptions ?? {
agent: 'classic',
skipCompletion: false,
model: 'gpt-3.5-turbo',
temperature: 0,
// top_p: 1,

View file

@ -68,6 +68,7 @@ const buildDefaultConversation = ({
} else if (endpoint === 'gptPlugins') {
const agentOptions = lastConversationSetup?.agentOptions ?? {
agent: 'classic',
skipCompletion: false,
model: 'gpt-3.5-turbo',
temperature: 0,
// top_p: 1,

View file

@ -89,6 +89,7 @@ const useMessageHandler = () => {
} else if (endpoint === 'gptPlugins') {
const agentOptions = currentConversation?.agentOptions ?? {
agent: 'classic',
skipCompletion: false,
model: 'gpt-3.5-turbo',
temperature: 0,
// top_p: 1,

48
package-lock.json generated
View file

@ -369,6 +369,7 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-slider": "^1.1.1",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.3",
"@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.28.0",
@ -6506,6 +6507,35 @@
}
}
},
"node_modules/@radix-ui/react-switch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz",
"integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-controllable-state": "1.0.1",
"@radix-ui/react-use-previous": "1.0.1",
"@radix-ui/react-use-size": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tabs": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz",
@ -29519,7 +29549,7 @@
"jsonwebtoken": "^9.0.0",
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
"langchain": "0.0.94",
"langchain": "^0.0.94",
"lodash": "^4.17.21",
"meilisearch": "^0.33.0",
"mongoose": "^7.1.1",
@ -29614,6 +29644,7 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-slider": "^1.1.1",
"@radix-ui/react-switch": "*",
"@radix-ui/react-tabs": "^1.0.3",
"@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.28.0",
@ -30152,6 +30183,21 @@
"@radix-ui/react-compose-refs": "1.0.1"
}
},
"@radix-ui/react-switch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz",
"integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-controllable-state": "1.0.1",
"@radix-ui/react-use-previous": "1.0.1",
"@radix-ui/react-use-size": "1.0.1"
}
},
"@radix-ui/react-tabs": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz",

View file

@ -61,7 +61,8 @@
"nodemonConfig": {
"ignore": [
"api/data/",
"data"
"data",
"client/"
]
}
}