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'); } = require('@dqbd/tiktoken');
const { fetchEventSource } = require('@waylaidwanderer/fetch-event-source'); const { fetchEventSource } = require('@waylaidwanderer/fetch-event-source');
const { Agent, ProxyAgent } = require('undici'); const { Agent, ProxyAgent } = require('undici');
// const TextStream = require('../stream'); const TextStream = require('../stream');
const { ChatOpenAI } = require('langchain/chat_models/openai'); const { ChatOpenAI } = require('langchain/chat_models/openai');
const { CallbackManager } = require('langchain/callbacks'); const { CallbackManager } = require('langchain/callbacks');
const { HumanChatMessage, AIChatMessage } = require('langchain/schema'); 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 }; return { ...responseMessage, ...this.result };
} }
// if (!this.agentIsGpt3 && this.result.output) { if (!completionMode && this.agentOptions.skipCompletion && this.result.output) {
// responseMessage.text = this.result.output; responseMessage.text = this.result.output;
// await this.saveMessageToDatabase(responseMessage, user); this.addImages(this.result.intermediateSteps, responseMessage);
// const textStream = new TextStream(this.result.output); await this.saveMessageToDatabase(responseMessage, user);
// await textStream.processTextStream(opts.onProgress); const textStream = new TextStream(this.result.output);
// return { ...responseMessage, ...this.result }; await textStream.processTextStream(opts.onProgress);
// } return { ...responseMessage, ...this.result };
}
if (this.options.debug) { if (this.options.debug) {
console.debug('this.result', this.result); 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 }; 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 }) { async buildPrompt({ messages, promptPrefix: _promptPrefix, completionMode = false, isChatGptModel = true }) {
if (this.options.debug) { if (this.options.debug) {
console.debug('buildPrompt messages', messages); console.debug('buildPrompt messages', messages);

View file

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

View file

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

View file

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

View file

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

View file

@ -188,6 +188,7 @@ function PluginsOptions() {
{showAgentSettings ? ( {showAgentSettings ? (
<AgentSettings <AgentSettings
agent={agentOptions.agent} agent={agentOptions.agent}
skipCompletion={agentOptions.skipCompletion}
model={agentOptions.model} model={agentOptions.model}
endpoint={agentOptions.endpoint} endpoint={agentOptions.endpoint}
temperature={agentOptions.temperature} 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 './ModelSelect';
export * from './Prompt'; export * from './Prompt';
export * from './Slider'; export * from './Slider';
export * from './Switch';
export * from './Tabs'; export * from './Tabs';
export * from './Templates'; export * from './Templates';
export * from './Textarea'; export * from './Textarea';

View file

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

View file

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

View file

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

48
package-lock.json generated
View file

@ -369,6 +369,7 @@
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.0", "@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-slider": "^1.1.1", "@radix-ui/react-slider": "^1.1.1",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.3", "@radix-ui/react-tabs": "^1.0.3",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.28.0", "@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": { "node_modules/@radix-ui/react-tabs": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz",
@ -29519,7 +29549,7 @@
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"keyv": "^4.5.2", "keyv": "^4.5.2",
"keyv-file": "^0.2.0", "keyv-file": "^0.2.0",
"langchain": "0.0.94", "langchain": "^0.0.94",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"meilisearch": "^0.33.0", "meilisearch": "^0.33.0",
"mongoose": "^7.1.1", "mongoose": "^7.1.1",
@ -29614,6 +29644,7 @@
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.0", "@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-slider": "^1.1.1", "@radix-ui/react-slider": "^1.1.1",
"@radix-ui/react-switch": "*",
"@radix-ui/react-tabs": "^1.0.3", "@radix-ui/react-tabs": "^1.0.3",
"@tailwindcss/forms": "^0.5.3", "@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.28.0", "@tanstack/react-query": "^4.28.0",
@ -30152,6 +30183,21 @@
"@radix-ui/react-compose-refs": "1.0.1" "@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": { "@radix-ui/react-tabs": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz",

View file

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