🤖 feat: Add Agents librechat.yaml Configuration (#4953)

* feat: CONFIG_VERSION v1.1.9, agents config

* refactor: Assistants Code Interpreter Toggle Improved Accessibility

* feat: Agents Config
This commit is contained in:
Danny Avila 2024-12-12 08:58:00 -05:00 committed by GitHub
parent 51e016ef2c
commit e82af236bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 65 additions and 48 deletions

View file

@ -27,6 +27,15 @@ async function endpointController(req, res) {
capabilities, capabilities,
}; };
} }
if (mergedConfig[EModelEndpoint.agents] && req.app.locals?.[EModelEndpoint.agents]) {
const { disableBuilder, capabilities, ..._rest } = req.app.locals[EModelEndpoint.agents];
mergedConfig[EModelEndpoint.agents] = {
...mergedConfig[EModelEndpoint.agents],
disableBuilder,
capabilities,
};
}
if ( if (
mergedConfig[EModelEndpoint.azureAssistants] && mergedConfig[EModelEndpoint.azureAssistants] &&

View file

@ -7,6 +7,7 @@ const handleRateLimits = require('./Config/handleRateLimits');
const { loadDefaultInterface } = require('./start/interface'); const { loadDefaultInterface } = require('./start/interface');
const { azureConfigSetup } = require('./start/azureOpenAI'); const { azureConfigSetup } = require('./start/azureOpenAI');
const { loadAndFormatTools } = require('./ToolService'); const { loadAndFormatTools } = require('./ToolService');
const { agentsConfigSetup } = require('./start/agents');
const { initializeRoles } = require('~/models/Role'); const { initializeRoles } = require('~/models/Role');
const paths = require('~/config/paths'); const paths = require('~/config/paths');
@ -94,6 +95,10 @@ const AppService = async (app) => {
); );
} }
if (endpoints?.[EModelEndpoint.agents]) {
endpointLocals[EModelEndpoint.agents] = agentsConfigSetup(config);
}
const endpointKeys = [ const endpointKeys = [
EModelEndpoint.openAI, EModelEndpoint.openAI,
EModelEndpoint.google, EModelEndpoint.google,

View file

@ -0,0 +1,14 @@
const { EModelEndpoint, agentsEndpointSChema } = require('librechat-data-provider');
/**
* Sets up the Agents configuration from the config (`librechat.yaml`) file.
* @param {TCustomConfig} config - The loaded custom configuration.
* @returns {Partial<TAgentsEndpoint>} The Agents endpoint configuration.
*/
function agentsConfigSetup(config) {
const agentsConfig = config.endpoints[EModelEndpoint.agents];
const parsedConfig = agentsEndpointSChema.parse(agentsConfig);
return parsedConfig;
}
module.exports = { agentsConfigSetup };

View file

@ -819,11 +819,17 @@
*/ */
/** /**
* @exports TAssistantEndpoint * @exports TAgentsEndpoint
* @typedef {import('librechat-data-provider').TAssistantEndpoint} TAssistantEndpoint * @typedef {import('librechat-data-provider').TAssistantEndpoint} TAssistantEndpoint
* @memberof typedefs * @memberof typedefs
*/ */
/**
* @exports TAgentsEndpoint
* @typedef {import('librechat-data-provider').TAgentsEndpoint} TAgentsEndpoint
* @memberof typedefs
*/
/** /**
* @exports Agent * @exports Agent
* @typedef {import('librechat-data-provider').Agent} Agent * @typedef {import('librechat-data-provider').Agent} Agent

View file

@ -173,6 +173,7 @@ export type AgentPanelProps = {
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>; setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
endpointsConfig?: t.TEndpointsConfig; endpointsConfig?: t.TEndpointsConfig;
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>; setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
agentsConfig?: t.TAgentsEndpoint | null;
}; };
export type AgentModelPanelProps = { export type AgentModelPanelProps = {

View file

@ -9,7 +9,7 @@ import {
PermissionTypes, PermissionTypes,
AgentCapabilities, AgentCapabilities,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import type { TConfig, TPlugin } from 'librechat-data-provider'; import type { TPlugin } from 'librechat-data-provider';
import type { AgentForm, AgentPanelProps } from '~/common'; import type { AgentForm, AgentPanelProps } from '~/common';
import { cn, defaultTextProps, removeFocusOutlines, getEndpointField, getIconKey } from '~/utils'; import { cn, defaultTextProps, removeFocusOutlines, getEndpointField, getIconKey } from '~/utils';
import { useCreateAgentMutation, useUpdateAgentMutation } from '~/data-provider'; import { useCreateAgentMutation, useUpdateAgentMutation } from '~/data-provider';
@ -43,7 +43,7 @@ export default function AgentConfig({
endpointsConfig, endpointsConfig,
setActivePanel, setActivePanel,
setCurrentAgentId, setCurrentAgentId,
}: AgentPanelProps & { agentsConfig?: TConfig | null }) { }: AgentPanelProps) {
const { user } = useAuthContext(); const { user } = useAuthContext();
const fileMap = useFileMapContext(); const fileMap = useFileMapContext();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -69,19 +69,19 @@ export default function AgentConfig({
}); });
const toolsEnabled = useMemo( const toolsEnabled = useMemo(
() => agentsConfig?.capabilities?.includes(AgentCapabilities.tools), () => agentsConfig?.capabilities.includes(AgentCapabilities.tools),
[agentsConfig], [agentsConfig],
); );
const actionsEnabled = useMemo( const actionsEnabled = useMemo(
() => agentsConfig?.capabilities?.includes(AgentCapabilities.actions), () => agentsConfig?.capabilities.includes(AgentCapabilities.actions),
[agentsConfig], [agentsConfig],
); );
const fileSearchEnabled = useMemo( const fileSearchEnabled = useMemo(
() => agentsConfig?.capabilities?.includes(AgentCapabilities.file_search) ?? false, () => agentsConfig?.capabilities.includes(AgentCapabilities.file_search) ?? false,
[agentsConfig], [agentsConfig],
); );
const codeEnabled = useMemo( const codeEnabled = useMemo(
() => agentsConfig?.capabilities?.includes(AgentCapabilities.execute_code) ?? false, () => agentsConfig?.capabilities.includes(AgentCapabilities.execute_code) ?? false,
[agentsConfig], [agentsConfig],
); );

View file

@ -8,7 +8,6 @@ import {
isAssistantsEndpoint, isAssistantsEndpoint,
defaultAgentFormValues, defaultAgentFormValues,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import type { TConfig } from 'librechat-data-provider';
import type { AgentForm, AgentPanelProps, StringOption } from '~/common'; import type { AgentForm, AgentPanelProps, StringOption } from '~/common';
import { import {
useCreateAgentMutation, useCreateAgentMutation,
@ -33,7 +32,7 @@ export default function AgentPanel({
setCurrentAgentId, setCurrentAgentId,
agentsConfig, agentsConfig,
endpointsConfig, endpointsConfig,
}: AgentPanelProps & { agentsConfig?: TConfig | null }) { }: AgentPanelProps) {
const localize = useLocalize(); const localize = useLocalize();
const { user } = useAuthContext(); const { user } = useAuthContext();
const { showToast } = useToastContext(); const { showToast } = useToastContext();

View file

@ -30,26 +30,29 @@ export default function Code({ version }: { version: number | string }) {
checked={field.value} checked={field.value}
onCheckedChange={field.onChange} onCheckedChange={field.onChange}
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer" className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
value={field?.value?.toString()} value={field.value.toString()}
/> />
)} )}
/> />
<div className="flex items-center space-x-2"> <button
type="button"
className="flex items-center space-x-2"
onClick={() =>
setValue(Capabilities.code_interpreter, !getValues(Capabilities.code_interpreter), {
shouldDirty: true,
})
}
>
<label <label
className="form-check-label text-token-text-primary w-full cursor-pointer" className="form-check-label text-token-text-primary w-full cursor-pointer"
htmlFor={Capabilities.code_interpreter} htmlFor={Capabilities.code_interpreter}
onClick={() =>
setValue(Capabilities.code_interpreter, !getValues(Capabilities.code_interpreter), {
shouldDirty: true,
})
}
> >
{localize('com_assistants_code_interpreter')} {localize('com_assistants_code_interpreter')}
</label> </label>
<HoverCardTrigger> <HoverCardTrigger>
<CircleHelpIcon className="h-5 w-5 text-gray-500" /> <CircleHelpIcon className="h-5 w-5 text-gray-500" />
</HoverCardTrigger> </HoverCardTrigger>
</div> </button>
<HoverCardPortal> <HoverCardPortal>
<HoverCardContent side={ESide.Top} className="w-80"> <HoverCardContent side={ESide.Top} className="w-80">
<div className="space-y-2"> <div className="space-y-2">

View file

@ -76,7 +76,7 @@ export default function useSideNavLinks({
hasAccessToCreateAgents && hasAccessToCreateAgents &&
isAgentsEndpoint(endpoint) && isAgentsEndpoint(endpoint) &&
agents && agents &&
// agents.disableBuilder !== true && agents.disableBuilder !== true &&
keyProvided && keyProvided &&
interfaceConfig.parameters === true interfaceConfig.parameters === true
) { ) {

View file

@ -119,7 +119,7 @@ export default {
com_agents_enable_file_search: 'Enable File Search', com_agents_enable_file_search: 'Enable File Search',
com_agents_file_search_info: com_agents_file_search_info:
'When enabled, the agent will be informed of the exact filenames listed below, allowing it to retrieve relevant context from these files.', 'When enabled, the agent will be informed of the exact filenames listed below, allowing it to retrieve relevant context from these files.',
com_agents_code_interpreter_title: 'Code Interpreter', com_agents_code_interpreter_title: 'Code Interpreter API',
com_agents_by_librechat: 'by LibreChat', com_agents_by_librechat: 'by LibreChat',
com_agents_code_interpreter: com_agents_code_interpreter:
'When enabled, allows your agent to leverage the LibreChat Code Interpreter API to run generated code, including file processing, securely. Requires a valid API key.', 'When enabled, allows your agent to leverage the LibreChat Code Interpreter API to run generated code, including file processing, securely. Requires a valid API key.',

2
package-lock.json generated
View file

@ -36153,7 +36153,7 @@
}, },
"packages/data-provider": { "packages/data-provider": {
"name": "librechat-data-provider", "name": "librechat-data-provider",
"version": "0.7.60", "version": "0.7.61",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",

View file

@ -1,6 +1,6 @@
{ {
"name": "librechat-data-provider", "name": "librechat-data-provider",
"version": "0.7.60", "version": "0.7.61",
"description": "data services for librechat apps", "description": "data services for librechat apps",
"main": "dist/index.js", "main": "dist/index.js",
"module": "dist/index.es.js", "module": "dist/index.es.js",

View file

@ -208,37 +208,17 @@ export type TAssistantEndpoint = z.infer<typeof assistantEndpointSchema>;
export const agentsEndpointSChema = baseEndpointSchema.merge( export const agentsEndpointSChema = baseEndpointSchema.merge(
z.object({ z.object({
/* assistants specific */ /* agents specific */
disableBuilder: z.boolean().optional(), disableBuilder: z.boolean().optional(),
pollIntervalMs: z.number().optional(),
timeoutMs: z.number().optional(),
version: z.union([z.string(), z.number()]).default(2),
supportedIds: z.array(z.string()).min(1).optional(),
excludedIds: z.array(z.string()).min(1).optional(),
privateAssistants: z.boolean().optional(),
retrievalModels: z.array(z.string()).min(1).optional().default(defaultRetrievalModels),
capabilities: z capabilities: z
.array(z.nativeEnum(Capabilities)) .array(z.nativeEnum(AgentCapabilities))
.optional() .optional()
.default([ .default([
Capabilities.code_interpreter, AgentCapabilities.execute_code,
Capabilities.image_vision, AgentCapabilities.file_search,
Capabilities.retrieval, AgentCapabilities.actions,
Capabilities.actions, AgentCapabilities.tools,
Capabilities.tools,
]), ]),
/* general */
apiKey: z.string().optional(),
models: z
.object({
default: z.array(z.string()).min(1),
fetch: z.boolean().optional(),
userIdQuery: z.boolean().optional(),
})
.optional(),
titleConvo: z.boolean().optional(),
titleMethod: z.union([z.literal('completion'), z.literal('functions')]).optional(),
headers: z.record(z.any()).optional(),
}), }),
); );
@ -1097,7 +1077,7 @@ export enum Constants {
/** Key for the app's version. */ /** Key for the app's version. */
VERSION = 'v0.7.5', VERSION = 'v0.7.5',
/** Key for the Custom Config's version (librechat.yaml). */ /** Key for the Custom Config's version (librechat.yaml). */
CONFIG_VERSION = '1.1.8', CONFIG_VERSION = '1.1.9',
/** Standard value for the first message's `parentMessageId` value, to indicate no parent exists. */ /** Standard value for the first message's `parentMessageId` value, to indicate no parent exists. */
NO_PARENT = '00000000-0000-0000-0000-000000000000', NO_PARENT = '00000000-0000-0000-0000-000000000000',
/** Standard value for the initial conversationId before a request is sent */ /** Standard value for the initial conversationId before a request is sent */