feat: Add Current Datetime to Assistants (v1/v2) (#4952)

* Feature: Added ability to send current date and time to v1 and v2 assistants

* remove date_feature.patch

* fix: rename append_today_date to append_current_datetime

* feat: Refactor time handling in chatV1 and chatV2, add date and time utility functions

* fix: Add warning log and response for missing run values in abortRun middleware

---------

Co-authored-by: Max Sanna <max@maxsanna.com>
This commit is contained in:
Danny Avila 2024-12-11 15:26:18 -05:00 committed by GitHub
parent b5c9144127
commit 1dbe6ee75d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 378 additions and 67 deletions

View file

@ -28,6 +28,10 @@ const assistantSchema = mongoose.Schema(
},
file_ids: { type: [String], default: undefined },
actions: { type: [String], default: undefined },
append_current_datetime: {
type: Boolean,
default: false,
},
},
{
timestamps: true,

View file

@ -1,5 +1,6 @@
const { v4 } = require('uuid');
const {
Time,
Constants,
RunStatus,
CacheKeys,
@ -24,6 +25,7 @@ const validateAuthor = require('~/server/middleware/assistants/validateAuthor');
const { formatMessage, createVisionPrompt } = require('~/app/clients/prompts');
const { createRun, StreamRunManager } = require('~/server/services/Runs');
const { addTitle } = require('~/server/services/Endpoints/assistants');
const { createRunBody } = require('~/server/services/createRunBody');
const { getTransactions } = require('~/models/Transaction');
const checkBalance = require('~/models/checkBalance');
const { getConvo } = require('~/models/Conversation');
@ -32,8 +34,6 @@ const { getModelMaxTokens } = require('~/utils');
const { getOpenAIClient } = require('./helpers');
const { logger } = require('~/config');
const ten_minutes = 1000 * 60 * 10;
/**
* @route POST /
* @desc Chat with an assistant
@ -59,6 +59,7 @@ const chatV1 = async (req, res) => {
messageId: _messageId,
conversationId: convoId,
parentMessageId: _parentId = Constants.NO_PARENT,
clientTimestamp,
} = req.body;
/** @type {OpenAIClient} */
@ -304,24 +305,14 @@ const chatV1 = async (req, res) => {
};
/** @type {CreateRunBody | undefined} */
const body = {
const body = createRunBody({
assistant_id,
model,
};
if (promptPrefix) {
body.additional_instructions = promptPrefix;
}
if (typeof endpointOption.artifactsPrompt === 'string' && endpointOption.artifactsPrompt) {
body.additional_instructions = `${body.additional_instructions ?? ''}\n${
endpointOption.artifactsPrompt
}`.trim();
}
if (instructions) {
body.instructions = instructions;
}
promptPrefix,
instructions,
endpointOption,
clientTimestamp,
});
const getRequestFileIds = async () => {
let thread_file_ids = [];
@ -518,7 +509,7 @@ const chatV1 = async (req, res) => {
});
run_id = run.id;
await cache.set(cacheKey, `${thread_id}:${run_id}`, ten_minutes);
await cache.set(cacheKey, `${thread_id}:${run_id}`, Time.TEN_MINUTES);
sendInitialResponse();
// todo: retry logic
@ -529,7 +520,7 @@ const chatV1 = async (req, res) => {
/** @type {{[AssistantStreamEvents.ThreadRunCreated]: (event: ThreadRunCreated) => Promise<void>}} */
const handlers = {
[AssistantStreamEvents.ThreadRunCreated]: async (event) => {
await cache.set(cacheKey, `${thread_id}:${event.data.id}`, ten_minutes);
await cache.set(cacheKey, `${thread_id}:${event.data.id}`, Time.TEN_MINUTES);
run_id = event.data.id;
sendInitialResponse();
},

View file

@ -23,6 +23,7 @@ const { createErrorHandler } = require('~/server/controllers/assistants/errors')
const validateAuthor = require('~/server/middleware/assistants/validateAuthor');
const { createRun, StreamRunManager } = require('~/server/services/Runs');
const { addTitle } = require('~/server/services/Endpoints/assistants');
const { createRunBody } = require('~/server/services/createRunBody');
const { getTransactions } = require('~/models/Transaction');
const checkBalance = require('~/models/checkBalance');
const { getConvo } = require('~/models/Conversation');
@ -31,8 +32,6 @@ const { getModelMaxTokens } = require('~/utils');
const { getOpenAIClient } = require('./helpers');
const { logger } = require('~/config');
const ten_minutes = 1000 * 60 * 10;
/**
* @route POST /
* @desc Chat with an assistant
@ -58,6 +57,7 @@ const chatV2 = async (req, res) => {
messageId: _messageId,
conversationId: convoId,
parentMessageId: _parentId = Constants.NO_PARENT,
clientTimestamp,
} = req.body;
/** @type {OpenAIClient} */
@ -186,22 +186,14 @@ const chatV2 = async (req, res) => {
};
/** @type {CreateRunBody | undefined} */
const body = {
const body = createRunBody({
assistant_id,
model,
};
if (promptPrefix) {
body.additional_instructions = promptPrefix;
}
if (typeof endpointOption.artifactsPrompt === 'string' && endpointOption.artifactsPrompt) {
body.additional_instructions = `${body.additional_instructions ?? ''}\n${endpointOption.artifactsPrompt}`.trim();
}
if (instructions) {
body.instructions = instructions;
}
promptPrefix,
instructions,
endpointOption,
clientTimestamp,
});
const getRequestFileIds = async () => {
let thread_file_ids = [];
@ -361,7 +353,7 @@ const chatV2 = async (req, res) => {
});
run_id = run.id;
await cache.set(cacheKey, `${thread_id}:${run_id}`, ten_minutes);
await cache.set(cacheKey, `${thread_id}:${run_id}`, Time.TEN_MINUTES);
sendInitialResponse();
// todo: retry logic
@ -372,7 +364,7 @@ const chatV2 = async (req, res) => {
/** @type {{[AssistantStreamEvents.ThreadRunCreated]: (event: ThreadRunCreated) => Promise<void>}} */
const handlers = {
[AssistantStreamEvents.ThreadRunCreated]: async (event) => {
await cache.set(cacheKey, `${thread_id}:${event.data.id}`, ten_minutes);
await cache.set(cacheKey, `${thread_id}:${event.data.id}`, Time.TEN_MINUTES);
run_id = event.data.id;
sendInitialResponse();
},

View file

@ -19,8 +19,15 @@ const createAssistant = async (req, res) => {
try {
const { openai } = await getOpenAIClient({ req, res });
const { tools = [], endpoint, conversation_starters, ...assistantData } = req.body;
const {
tools = [],
endpoint,
conversation_starters,
append_current_datetime,
...assistantData
} = req.body;
delete assistantData.conversation_starters;
delete assistantData.append_current_datetime;
assistantData.tools = tools
.map((tool) => {
@ -49,6 +56,9 @@ const createAssistant = async (req, res) => {
if (conversation_starters) {
createData.conversation_starters = conversation_starters;
}
if (append_current_datetime !== undefined) {
createData.append_current_datetime = append_current_datetime;
}
const document = await updateAssistantDoc({ assistant_id: assistant.id }, createData);
@ -60,6 +70,10 @@ const createAssistant = async (req, res) => {
assistant.conversation_starters = document.conversation_starters;
}
if (append_current_datetime !== undefined) {
assistant.append_current_datetime = append_current_datetime;
}
logger.debug('/assistants/', assistant);
res.status(201).json(assistant);
} catch (error) {
@ -102,7 +116,12 @@ const patchAssistant = async (req, res) => {
await validateAuthor({ req, openai });
const assistant_id = req.params.id;
const { endpoint: _e, conversation_starters, ...updateData } = req.body;
const {
endpoint: _e,
conversation_starters,
append_current_datetime,
...updateData
} = req.body;
updateData.tools = (updateData.tools ?? [])
.map((tool) => {
if (typeof tool !== 'string') {
@ -127,6 +146,11 @@ const patchAssistant = async (req, res) => {
updatedAssistant.conversation_starters = conversationStartersUpdate.conversation_starters;
}
if (append_current_datetime !== undefined) {
await updateAssistantDoc({ assistant_id }, { append_current_datetime });
updatedAssistant.append_current_datetime = append_current_datetime;
}
res.json(updatedAssistant);
} catch (error) {
logger.error('[/assistants/:id] Error updating assistant', error);
@ -219,6 +243,7 @@ const getAssistantDocuments = async (req, res) => {
conversation_starters: 1,
createdAt: 1,
updatedAt: 1,
append_current_datetime: 1,
},
);

View file

@ -16,8 +16,15 @@ const createAssistant = async (req, res) => {
/** @type {{ openai: OpenAIClient }} */
const { openai } = await getOpenAIClient({ req, res });
const { tools = [], endpoint, conversation_starters, ...assistantData } = req.body;
const {
tools = [],
endpoint,
conversation_starters,
append_current_datetime,
...assistantData
} = req.body;
delete assistantData.conversation_starters;
delete assistantData.append_current_datetime;
assistantData.tools = tools
.map((tool) => {
@ -46,6 +53,9 @@ const createAssistant = async (req, res) => {
if (conversation_starters) {
createData.conversation_starters = conversation_starters;
}
if (append_current_datetime !== undefined) {
createData.append_current_datetime = append_current_datetime;
}
const document = await updateAssistantDoc({ assistant_id: assistant.id }, createData);
@ -56,6 +66,9 @@ const createAssistant = async (req, res) => {
if (document.conversation_starters) {
assistant.conversation_starters = document.conversation_starters;
}
if (append_current_datetime !== undefined) {
assistant.append_current_datetime = append_current_datetime;
}
logger.debug('/assistants/', assistant);
res.status(201).json(assistant);
@ -89,6 +102,14 @@ const updateAssistant = async ({ req, openai, assistant_id, updateData }) => {
delete updateData.conversation_starters;
}
if (updateData?.append_current_datetime !== undefined) {
await updateAssistantDoc(
{ assistant_id: assistant_id },
{ append_current_datetime: updateData.append_current_datetime },
);
delete updateData.append_current_datetime;
}
let hasFileSearch = false;
for (const tool of updateData.tools ?? []) {
let actualTool = typeof tool === 'string' ? req.app.locals.availableTools[tool] : tool;

View file

@ -27,6 +27,10 @@ async function abortRun(req, res) {
const cacheKey = `${req.user.id}:${conversationId}`;
const cache = getLogStores(CacheKeys.ABORT_KEYS);
const runValues = await cache.get(cacheKey);
if (!runValues) {
logger.warn('[abortRun] Run not found in cache', { cacheKey });
return res.status(204).send({ message: 'Run not found' });
}
const [thread_id, run_id] = runValues.split(':');
if (!run_id) {

View file

@ -79,7 +79,7 @@ async function buildEndpointOption(req, res, next) {
const builder = isAgents ? (...args) => endpointFn(req, ...args) : endpointFn;
// TODO: use object params
req.body.endpointOption = builder(endpoint, parsedBody, endpointType);
req.body.endpointOption = await builder(endpoint, parsedBody, endpointType);
// TODO: use `getModelsConfig` only when necessary
const modelsConfig = await getModelsConfig(req);

View file

@ -1,7 +1,8 @@
const { removeNullishValues } = require('librechat-data-provider');
const generateArtifactsPrompt = require('~/app/clients/prompts/artifacts');
const { getAssistant } = require('~/models/Assistant');
const buildOptions = (endpoint, parsedBody) => {
const buildOptions = async (endpoint, parsedBody) => {
// eslint-disable-next-line no-unused-vars
const { promptPrefix, assistant_id, iconURL, greeting, spec, artifacts, ...modelOptions } =
parsedBody;
@ -15,6 +16,21 @@ const buildOptions = (endpoint, parsedBody) => {
modelOptions,
});
if (assistant_id) {
const assistantDoc = await getAssistant({ assistant_id });
if (assistantDoc) {
// Create a clean assistant object with only the needed properties
endpointOption.assistant = {
append_current_datetime: assistantDoc.append_current_datetime,
assistant_id: assistantDoc.assistant_id,
conversation_starters: assistantDoc.conversation_starters,
createdAt: assistantDoc.createdAt,
updatedAt: assistantDoc.updatedAt,
};
}
}
if (typeof artifacts === 'string') {
endpointOption.artifactsPrompt = generateArtifactsPrompt({ endpoint, artifacts });
}

View file

@ -1,7 +1,8 @@
const { removeNullishValues } = require('librechat-data-provider');
const generateArtifactsPrompt = require('~/app/clients/prompts/artifacts');
const { getAssistant } = require('~/models/Assistant');
const buildOptions = (endpoint, parsedBody) => {
const buildOptions = async (endpoint, parsedBody) => {
// eslint-disable-next-line no-unused-vars
const { promptPrefix, assistant_id, iconURL, greeting, spec, artifacts, ...modelOptions } =
parsedBody;
@ -15,6 +16,19 @@ const buildOptions = (endpoint, parsedBody) => {
modelOptions,
});
if (assistant_id) {
const assistantDoc = await getAssistant({ assistant_id });
if (assistantDoc) {
endpointOption.assistant = {
append_current_datetime: assistantDoc.append_current_datetime,
assistant_id: assistantDoc.assistant_id,
conversation_starters: assistantDoc.conversation_starters,
createdAt: assistantDoc.createdAt,
updatedAt: assistantDoc.updatedAt,
};
}
}
if (typeof artifacts === 'string') {
endpointOption.artifactsPrompt = generateArtifactsPrompt({ endpoint, artifacts });
}

View file

@ -0,0 +1,78 @@
/**
* Obtains the date string in 'YYYY-MM-DD' format.
*
* @param {string} [clientTimestamp] - Optional ISO timestamp string. If provided, uses this timestamp;
* otherwise, uses the current date.
* @returns {string} - The date string in 'YYYY-MM-DD' format.
*/
function getDateStr(clientTimestamp) {
return clientTimestamp ? clientTimestamp.split('T')[0] : new Date().toISOString().split('T')[0];
}
/**
* Obtains the time string in 'HH:MM:SS' format.
*
* @param {string} [clientTimestamp] - Optional ISO timestamp string. If provided, uses this timestamp;
* otherwise, uses the current time.
* @returns {string} - The time string in 'HH:MM:SS' format.
*/
function getTimeStr(clientTimestamp) {
return clientTimestamp
? clientTimestamp.split('T')[1].split('.')[0]
: new Date().toTimeString().split(' ')[0];
}
/**
* Creates the body object for a run request.
*
* @param {Object} options - The options for creating the run body.
* @param {string} options.assistant_id - The assistant ID.
* @param {string} options.model - The model name.
* @param {string} [options.promptPrefix] - The prompt prefix to include.
* @param {string} [options.instructions] - The instructions to include.
* @param {Object} [options.endpointOption={}] - The endpoint options.
* @param {string} [options.clientTimestamp] - Client timestamp in ISO format.
*
* @returns {Object} - The constructed body object for the run request.
*/
const createRunBody = ({
assistant_id,
model,
promptPrefix,
instructions,
endpointOption = {},
clientTimestamp,
}) => {
const body = {
assistant_id,
model,
};
let systemInstructions = '';
if (endpointOption.assistant?.append_current_datetime) {
const dateStr = getDateStr(clientTimestamp);
const timeStr = getTimeStr(clientTimestamp);
systemInstructions = `Current date and time: ${dateStr} ${timeStr}\n`;
}
if (promptPrefix) {
systemInstructions += promptPrefix;
}
if (typeof endpointOption?.artifactsPrompt === 'string' && endpointOption.artifactsPrompt) {
systemInstructions += `\n${endpointOption.artifactsPrompt}`;
}
if (systemInstructions.trim()) {
body.additional_instructions = systemInstructions.trim();
}
if (instructions) {
body.instructions = instructions;
}
return body;
};
module.exports = { createRunBody, getDateStr, getTimeStr };

View file

@ -27,4 +27,5 @@ export type AssistantForm = {
conversation_starters: string[];
model: string;
functions: string[];
append_current_datetime: boolean;
} & Actions;

View file

@ -278,6 +278,7 @@ export type TAskProps = {
parentMessageId?: string | null;
conversationId?: string | null;
messageId?: string | null;
clientTimestamp?: string;
};
export type TOptions = {

View file

@ -0,0 +1,76 @@
import { Control, Controller, UseFormSetValue, UseFormGetValues } from 'react-hook-form';
import { CircleHelpIcon } from '~/components/svg';
import { useLocalize } from '~/hooks';
import {
HoverCard,
HoverCardContent,
HoverCardPortal,
HoverCardTrigger,
Checkbox,
} from '~/components/ui';
import { ESide } from '~/common';
import type { AssistantForm } from '~/common';
interface AppendDateCheckboxProps {
control: Control<AssistantForm>;
setValue: UseFormSetValue<AssistantForm>;
getValues: UseFormGetValues<AssistantForm>;
}
export default function AppendDateCheckbox({ control, setValue }: AppendDateCheckboxProps) {
const localize = useLocalize();
const handleChange = (checked: boolean) => {
setValue('append_current_datetime', checked, {
shouldDirty: true,
});
};
return (
<div className="mb-6">
<HoverCard openDelay={50}>
<div className="flex items-center">
<Controller
name="append_current_datetime"
control={control}
render={({ field }) => (
<Checkbox
{...field}
id="append_current_datetime"
checked={field.value}
onCheckedChange={handleChange}
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
value={field.value.toString()}
aria-labelledby="append-date-label"
/>
)}
/>
<div className="flex items-center space-x-2">
<label
id="append-date-label"
htmlFor="append_current_datetime"
className="form-check-label text-token-text-primary w-full cursor-pointer"
>
{localize('com_assistants_append_date')}
</label>
<HoverCardTrigger>
<CircleHelpIcon
className="h-5 w-5 text-gray-500"
aria-label={localize('com_assistants_append_date_tooltip')}
/>
</HoverCardTrigger>
</div>
<HoverCardPortal>
<HoverCardContent side={ESide.Top} className="w-80">
<div className="space-y-2">
<p className="text-sm text-gray-600 dark:text-gray-300">
{localize('com_assistants_append_date_tooltip')}
</p>
</div>
</HoverCardContent>
</HoverCardPortal>
</div>
</HoverCard>
</div>
);
}

View file

@ -19,6 +19,7 @@ import { useAssistantsMapContext, useToastContext } from '~/Providers';
import { useSelectAssistant, useLocalize } from '~/hooks';
import { ToolSelectDialog } from '~/components/Tools';
import CapabilitiesForm from './CapabilitiesForm';
import AppendDateCheckbox from './AppendDateCheckbox';
import { SelectDropDown } from '~/components/ui';
import AssistantAvatar from './AssistantAvatar';
import AssistantSelect from './AssistantSelect';
@ -63,7 +64,7 @@ export default function AssistantPanel({
const [showToolDialog, setShowToolDialog] = useState(false);
const { control, handleSubmit, reset } = methods;
const { control, handleSubmit, reset, setValue, getValues } = methods;
const assistant = useWatch({ control, name: 'assistant' });
const functions = useWatch({ control, name: 'functions' });
const assistant_id = useWatch({ control, name: 'id' });
@ -167,7 +168,7 @@ export default function AssistantPanel({
instructions,
conversation_starters: starters,
model,
// file_ids, // TODO: add file handling here
append_current_datetime,
} = data;
if (assistant_id) {
@ -181,6 +182,7 @@ export default function AssistantPanel({
model,
tools,
endpoint,
append_current_datetime,
},
});
return;
@ -195,6 +197,7 @@ export default function AssistantPanel({
tools,
endpoint,
version,
append_current_datetime,
});
};
@ -325,6 +328,9 @@ export default function AssistantPanel({
/>
</div>
{/* Append Today's Date */}
<AppendDateCheckbox control={control} setValue={setValue} getValues={getValues} />
{/* Conversation Starters */}
<div className="relative mb-6">
{/* the label of conversation starters is in the component */}

View file

@ -1,5 +1,5 @@
import { Plus } from 'lucide-react';
import { useCallback, useEffect, useRef, useMemo } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import {
Tools,
FileSources,
@ -37,6 +37,7 @@ const keys = new Set([
'instructions',
'conversation_starters',
'model',
'append_current_datetime',
]);
export default function AssistantSelect({
@ -69,7 +70,7 @@ export default function AssistantSelect({
res.data.map((_assistant) => {
const source =
endpoint === EModelEndpoint.assistants ? FileSources.openai : FileSources.azure;
const assistant = {
const assistant: TAssistantOption = {
..._assistant,
label: _assistant.name ?? '',
value: _assistant.id,
@ -125,8 +126,11 @@ export default function AssistantSelect({
const assistantDoc = documentsMap?.get(_assistant.id);
/* If no user updates, use the latest assistant docs */
if (assistantDoc && !assistant.conversation_starters) {
assistant.conversation_starters = assistantDoc.conversation_starters;
if (assistantDoc) {
if (!assistant.conversation_starters) {
assistant.conversation_starters = assistantDoc.conversation_starters;
}
assistant.append_current_datetime = assistantDoc.append_current_datetime ?? false;
}
return assistant;
@ -184,6 +188,11 @@ export default function AssistantSelect({
return;
}
if (name === 'append_current_datetime') {
formValues[name] = !!value;
return;
}
if (
name === 'conversation_starters' &&
Array.isArray(value) &&

View file

@ -258,6 +258,8 @@ export const useCreateSharedLinkMutation = (
return;
}
const isPublic = vars.isPublic === true;
queryClient.setQueryData<t.SharedLinkListData>([QueryKeys.sharedLinks], (sharedLink) => {
if (!sharedLink) {
return sharedLink;
@ -265,24 +267,22 @@ export const useCreateSharedLinkMutation = (
const pageSize = sharedLink.pages[0].pageSize as number;
return normalizeData(
// If the shared link is public, add it to the shared links cache list
vars.isPublic
? addSharedLink(sharedLink, _data)
: deleteSharedLink(sharedLink, _data.shareId),
isPublic ? addSharedLink(sharedLink, _data) : deleteSharedLink(sharedLink, _data.shareId),
InfiniteCollections.SHARED_LINKS,
pageSize,
);
});
queryClient.setQueryData([QueryKeys.sharedLinks, _data.shareId], _data);
if (!vars.isPublic) {
if (!isPublic) {
const current = queryClient.getQueryData<t.ConversationData>([QueryKeys.sharedLinks]);
refetch({
refetchPage: (page, index) => index === (current?.pages.length || 1) - 1,
refetchPage: (page, index) => index === ((current?.pages.length ?? 0) || 1) - 1,
});
}
onSuccess?.(_data, vars, context);
},
...(_options || {}),
..._options,
});
};
@ -298,6 +298,8 @@ export const useUpdateSharedLinkMutation = (
return;
}
const isPublic = vars.isPublic === true;
queryClient.setQueryData<t.SharedLinkListData>([QueryKeys.sharedLinks], (sharedLink) => {
if (!sharedLink) {
return sharedLink;
@ -305,7 +307,7 @@ export const useUpdateSharedLinkMutation = (
return normalizeData(
// If the shared link is public, add it to the shared links cache list.
vars.isPublic
isPublic
? // Even if the SharedLink data exists in the database, it is not registered in the cache when isPublic is false.
// Therefore, when isPublic is true, use addSharedLink instead of updateSharedLink.
addSharedLink(sharedLink, _data)
@ -316,16 +318,16 @@ export const useUpdateSharedLinkMutation = (
});
queryClient.setQueryData([QueryKeys.sharedLinks, _data.shareId], _data);
if (!vars.isPublic) {
if (!isPublic) {
const current = queryClient.getQueryData<t.ConversationData>([QueryKeys.sharedLinks]);
refetch({
refetchPage: (page, index) => index === (current?.pages.length || 1) - 1,
refetchPage: (page, index) => index === ((current?.pages.length ?? 0) || 1) - 1,
});
}
onSuccess?.(_data, vars, context);
},
...(_options || {}),
..._options,
});
};
@ -466,7 +468,11 @@ export const useDeleteTagInConversations = () => {
for (let pageIndex = 0; pageIndex < newData.pages.length; pageIndex++) {
const page = newData.pages[pageIndex];
page.conversations = page.conversations.map((conversation) => {
if (conversation.conversationId && conversation.tags?.includes(deletedTag)) {
if (
conversation.conversationId != null &&
conversation.conversationId &&
conversation.tags?.includes(deletedTag) === true
) {
conversationIdsWithTag.push(conversation.conversationId);
conversation.tags = conversation.tags.filter((t) => t !== deletedTag);
}
@ -833,11 +839,12 @@ export const useUpdateAssistantMutation = (
if (!prev) {
return prev;
}
prev.map((doc) => {
return prev.map((doc) => {
if (doc.assistant_id === variables.assistant_id) {
return {
...doc,
conversation_starters: updatedAssistant.conversation_starters,
append_current_datetime: variables.data.append_current_datetime,
};
}
return doc;
@ -976,7 +983,9 @@ export const useUpdateAction = (
}
return action;
})
.concat(variables.action_id ? [] : [updateActionResponse[2]]);
.concat(
variables.action_id != null && variables.action_id ? [] : [updateActionResponse[2]],
);
});
return options?.onSuccess?.(updateActionResponse, variables, context);
@ -1032,7 +1041,7 @@ export const useDeleteAction = (
return {
...assistant,
tools: (assistant.tools ?? []).filter(
(tool) => !tool.function?.name.includes(domain ?? ''),
(tool) => !(tool.function?.name.includes(domain ?? '') ?? false),
),
};
}
@ -1230,7 +1239,11 @@ export const useUpdateAgentAction = (
}
return action;
})
.concat(variables.action_id ? [] : [updateAgentActionResponse[1]]);
.concat(
variables.action_id != null && variables.action_id
? []
: [updateAgentActionResponse[1]],
);
});
return options?.onSuccess?.(updateAgentActionResponse, variables, context);

View file

@ -176,6 +176,7 @@ export default function useChatFunctions({
const currentMsg: TMessage = {
text,
sender: 'User',
clientTimestamp: new Date().toLocaleString('sv').replace(' ', 'T'),
isCreatedByUser: true,
parentMessageId,
conversationId,

View file

@ -45,17 +45,22 @@ export default function useSubmitMessage(helpers?: { clearDraft?: () => void })
const overrideConvoId = isNewMultiConvo ? v4() : undefined;
const overrideUserMessageId = hasAdded ? v4() : undefined;
const rootIndex = addedIndex - 1;
const clientTimestamp = new Date().toISOString();
ask({
text: data.text,
overrideConvoId: appendIndex(rootIndex, overrideConvoId),
overrideUserMessageId: appendIndex(rootIndex, overrideUserMessageId),
clientTimestamp,
});
if (hasAdded) {
askAdditional(
{
text: data.text,
overrideConvoId: appendIndex(addedIndex, overrideConvoId),
overrideUserMessageId: appendIndex(addedIndex, overrideUserMessageId),
clientTimestamp,
},
{ overrideMessages: rootMessages },
);

View file

@ -372,6 +372,9 @@ export default {
com_assistants_knowledge_disabled:
'يجب إنشاء المساعد وتمكين المفسر البرمجي أو الاسترجاع وحفظهما قبل تحميل الملفات كمعرفة.',
com_assistants_image_vision: 'رؤية الصورة',
com_assistants_append_date: 'إضافة التاريخ والوقت الحالي',
com_assistants_append_date_tooltip:
'عند التفعيل، سيتم إضافة التاريخ والوقت الحالي للعميل إلى تعليمات نظام المساعد.',
com_assistants_code_interpreter: 'مُفسِّر الشفرة',
com_assistants_code_interpreter_files: 'الملفات التالية متاحة فقط لمفسر الشفرة:',
com_assistants_retrieval: 'استرداد',

View file

@ -51,6 +51,9 @@ export default {
com_assistants_knowledge_disabled:
'O assistente deve ser criado, e o Interpretador de Código ou Recuperação deve ser habilitado e salvo antes de carregar arquivos como Conhecimento.',
com_assistants_image_vision: 'Visão de Imagem',
com_assistants_append_date: 'Anexar Data e Hora Atual',
com_assistants_append_date_tooltip:
'Quando ativado, a data e hora atual do cliente serão anexadas às instruções do sistema do assistente.',
com_assistants_code_interpreter: 'Interpretador de Código',
com_assistants_code_interpreter_files:
'Os arquivos abaixo são apenas para o Interpretador de Código:',

View file

@ -34,6 +34,9 @@ export default {
com_assistants_knowledge_disabled:
'Der Assistent muss erstellt und Code-Interpreter oder Abruf müssen aktiviert und gespeichert werden, bevor Dateien als Wissen hochgeladen werden können.',
com_assistants_image_vision: 'Bildanalyse',
com_assistants_append_date: 'Aktuelles Datum & Uhrzeit anhängen',
com_assistants_append_date_tooltip:
'Wenn aktiviert, werden das aktuelle Client-Datum und die Uhrzeit an die Systemanweisungen des Assistenten angehängt.',
com_assistants_code_interpreter: 'Code-Interpreter',
com_assistants_code_interpreter_files: 'Die folgenden Dateien sind nur für den Code-Interpreter:',
com_assistants_retrieval: 'Abruf',

View file

@ -70,6 +70,9 @@ export default {
com_assistants_knowledge_disabled:
'Assistant must be created, and Code Interpreter or Retrieval must be enabled and saved before uploading files as Knowledge.',
com_assistants_image_vision: 'Image Vision',
com_assistants_append_date: 'Append Current Date & Time',
com_assistants_append_date_tooltip:
'When enabled, the current client date and time will be appended to the assistant system instructions.',
com_assistants_code_interpreter: 'Code Interpreter',
com_assistants_code_interpreter_files: 'Files below are for Code Interpreter only:',
com_assistants_retrieval: 'Retrieval',

View file

@ -19,6 +19,9 @@ export default {
com_assistants_knowledge_disabled:
'El Asistente debe ser creado, y el Intérprete de Código o la Recuperación deben estar habilitados y guardados antes de subir archivos como Conocimiento.',
com_assistants_image_vision: 'Visión de Imagen',
com_assistants_append_date: 'Añadir Fecha y Hora Actual',
com_assistants_append_date_tooltip:
'Cuando está habilitado, la fecha y hora actual del cliente se adjuntarán a las instrucciones del sistema del asistente.',
com_assistants_code_interpreter: 'Intérprete de Código',
com_assistants_code_interpreter_files:
'Los siguientes archivos solo están disponibles para el Intérprete de Código:',

View file

@ -29,6 +29,9 @@ export default {
com_assistants_knowledge_disabled:
'Avustaja täytyy ensin luoda, ja Kooditulkki tai Tiedonhaku täytyy olla päällä ja asetukset tallennettuna, ennen kuin tiedostoja voidaan ladata Tietoihin.',
com_assistants_image_vision: 'Kuvanäkö',
com_assistants_append_date: 'Lisää nykyinen päivämäärä ja aika',
com_assistants_append_date_tooltip:
'Kun käytössä, nykyinen asiakkaan päivämäärä ja aika lisätään avustajan järjestelmäohjeisiin.',
com_assistants_code_interpreter: 'Kooditulkki',
com_assistants_code_interpreter_files: 'Seuraavat tiedostot ovat vain Kooditulkin käytettävissä:',
com_assistants_retrieval: 'Tiedonhaku',

View file

@ -479,6 +479,9 @@ export default {
com_assistants_knowledge_disabled:
'L\'assistant doit être créé, et l\'interpréteur de code ou la récupération doivent être activés et enregistrés avant de pouvoir importer des fichiers en tant que connaissances.',
com_assistants_image_vision: 'Vision d\'image',
com_assistants_append_date: 'Ajouter la date et l\'heure actuelles',
com_assistants_append_date_tooltip:
'Lorsque activé, la date et l\'heure actuelles du client seront ajoutées aux instructions du système de l\'assistant.',
com_assistants_code_interpreter: 'Interpréteur de code',
com_assistants_code_interpreter_files:
'Les fichiers suivants sont disponibles uniquement pour l\'interpréteur de code :',

View file

@ -11,6 +11,9 @@ export default {
'אם אתה מעלה קבצים תחת ידע, שיחות עם ה-סייען שלך עשויות לכלול תוכן מהקובץ.',
com_assistants_knowledge_disabled:
'יש ליצור סייען, ויש להפעיל ולשמור את מתורגמן קוד או אחזור לפני העלאת קבצים כ-ידע.',
com_assistants_append_date: 'הוסף תאריך ושעה נוכחיים',
com_assistants_append_date_tooltip:
'כשמופעל, תאריך ושעה נוכחיים של הלקוח יוספים להוראות מערכת הסייען.',
com_assistants_code_interpreter: 'מתורגמן קוד',
com_assistants_code_interpreter_files: 'הקבצים הבאים זמינים רק עבור מתורגמן קוד:',
com_assistants_retrieval: 'אחזור',

View file

@ -27,6 +27,9 @@ export default {
com_assistants_knowledge_disabled:
'L\'Assistente deve essere creato, e Code Interpreter o Retrieval devono essere abilitati e salvati prima di caricare file come Conoscenza.',
com_assistants_image_vision: 'Visione Immagine',
com_assistants_append_date: 'Aggiungi Data e Ora Attuali',
com_assistants_append_date_tooltip:
'Quando attivo, la data e l\'ora attuali del cliente saranno aggiunte alle istruzioni del sistema dell\'Assistente.',
com_assistants_code_interpreter: 'Interprete Codice',
com_assistants_code_interpreter_files:
'I seguenti file sono disponibili solo per Code Interpreter:',

View file

@ -48,6 +48,9 @@ export default {
com_assistants_knowledge_disabled:
'ファイルをナレッジとしてアップロードする前に、アシスタントを作成し、コードインタプリタまたは検索を有効にして保存する必要があります。',
com_assistants_image_vision: 'イメージビジョン',
com_assistants_append_date: '現在の日付と時刻を追加',
com_assistants_append_date_tooltip:
'有効にすると、現在のクライアントの日付と時刻がアシスタントのシステム指示に追加されます。',
com_assistants_code_interpreter: 'コードインタプリタ',
com_assistants_code_interpreter_files: '次のファイルはコードインタプリタでのみ使用できます。',
com_assistants_retrieval: '検索',

View file

@ -703,6 +703,9 @@ export default {
com_assistants_knowledge_disabled:
'지식으로 파일을 업로드하기 전에 Assistant를 생성하고 Code Interpreter 또는 Retrieval을 활성화한 후 저장해야 합니다.',
com_assistants_image_vision: '이미지 인식',
com_assistants_append_date: '현재 날짜와 시간 추가',
com_assistants_append_date_tooltip:
'활성화하면 현재 클라이언트의 날짜와 시간이 어시스턴트의 시스템 지침에 추가됩니다.',
com_assistants_code_interpreter: '코드 인터프리터',
com_assistants_code_interpreter_files: '코드 인터프리터에서만 다음 파일을 사용할 수 있습니다:',
com_assistants_retrieval: '검색',

View file

@ -5,6 +5,9 @@ export default {
com_sidepanel_assistant_builder: 'Конструктор Ассистента',
com_sidepanel_attach_files: 'Прикрепить файлы',
com_sidepanel_manage_files: 'Управление файлами',
com_assistants_append_date: 'Добавить текущую дату и время',
com_assistants_append_date_tooltip:
'Когда включено, текущая дата и время клиента будут добавлены к инструкциям системы Ассистента.',
com_assistants_code_interpreter: 'Интерпретатор кода',
com_assistants_code_interpreter_files: 'Следующие файлы доступны только для Интерпретатора кода:',
com_ui_examples: 'Примеры',

View file

@ -30,6 +30,9 @@ export default {
com_assistants_knowledge_disabled:
'Bilgi olarak dosya yüklemeden önce, Asistan oluşturulmalı ve Kod Yorumlayıcı veya Geri Getirme etkinleştirilip kaydedilmelidir.',
com_assistants_image_vision: 'Görüntü Vizyonu',
com_assistants_append_date: 'Şu anki tarih ve saati ekleyin',
com_assistants_append_date_tooltip:
'Etkinleştirildiğinde, şu anki müşteri tarihi ve saati, Asistanın sistem talimatlarına eklenir.',
com_assistants_code_interpreter: 'Kod Yorumlayıcı',
com_assistants_code_interpreter_files:
'Aşağıdaki dosyalar yalnızca Kod Yorumlayıcı için kullanılabilir:',

View file

@ -46,6 +46,8 @@ export default {
com_assistants_knowledge_disabled:
'必须创建助手,且启用并保存代码解释器或检索,才能将文件作为知识上传。',
com_assistants_image_vision: '识图',
com_assistants_append_date: '添加当前日期和时间',
com_assistants_append_date_tooltip: '启用后,当前客户的日期和时间将附加到助手的系统指令中。',
com_assistants_code_interpreter: '代码解释器',
com_assistants_code_interpreter_files: '以下文件仅适用于代码解释器:',
com_assistants_retrieval: '检索',

View file

@ -356,6 +356,8 @@ export default {
com_assistants_knowledge_disabled:
'助理必須先建立,並啟用及儲存「程式碼解譯器」或「資訊檢索」功能,才能上傳檔案作為知識庫。',
com_assistants_image_vision: '影像視覺',
com_assistants_append_date: '添加當前日期和時間',
com_assistants_append_date_tooltip: '啟用後,當前客戶的日期和時間將附加到助手的系統指令中。',
com_assistants_code_interpreter: '程式碼解譯器',
com_assistants_code_interpreter_files: '以下檔案僅適用於程式碼解譯器:',
com_assistants_retrieval: '檢索',

2
package-lock.json generated
View file

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

View file

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

View file

@ -128,6 +128,7 @@ export const defaultAssistantFormValues = {
code_interpreter: false,
image_vision: false,
retrieval: false,
append_current_datetime: false,
};
export const defaultAgentFormValues = {
@ -451,6 +452,7 @@ export const tMessageSchema = z.object({
isEdited: z.boolean().optional(),
isCreatedByUser: z.boolean(),
error: z.boolean().optional(),
clientTimestamp: z.string().optional(),
createdAt: z
.string()
.optional()
@ -485,6 +487,7 @@ export type TMessage = z.input<typeof tMessageSchema> & {
depth?: number;
siblingIndex?: number;
attachments?: TAttachment[];
clientTimestamp?: string;
};
export const coerceNumber = z.union([z.number(), z.string()]).transform((val) => {
@ -596,6 +599,7 @@ export const tConversationSchema = z.object({
agentOptions: tAgentOptionsSchema.nullable().optional(),
/** @deprecated Prefer `modelLabel` over `chatGptLabel` */
chatGptLabel: z.string().nullable().optional(),
append_current_datetime: z.boolean().optional(),
});
export const tPresetSchema = tConversationSchema
@ -849,6 +853,7 @@ export const assistantSchema = tConversationSchema
iconURL: true,
greeting: true,
spec: true,
append_current_datetime: true,
})
.transform((obj) => ({
...obj,
@ -859,6 +864,7 @@ export const assistantSchema = tConversationSchema
iconURL: obj.iconURL ?? undefined,
greeting: obj.greeting ?? undefined,
spec: obj.spec ?? undefined,
append_current_datetime: obj.append_current_datetime ?? false,
}))
.catch(() => ({
model: openAISettings.model.default,
@ -868,6 +874,7 @@ export const assistantSchema = tConversationSchema
iconURL: undefined,
greeting: undefined,
spec: undefined,
append_current_datetime: false,
}));
export const compactAssistantSchema = tConversationSchema

View file

@ -64,6 +64,7 @@ export type TSubmission = {
initialResponse?: TMessage;
conversation: Partial<TConversation>;
endpointOption: TEndpointOption;
clientTimestamp?: string;
};
export type EventSubmission = Omit<TSubmission, 'initialResponse'> & { initialResponse: TMessage };

View file

@ -100,6 +100,7 @@ export type AssistantCreateParams = {
tools?: Array<FunctionTool | string>;
endpoint: AssistantsEndpoint;
version: number | string;
append_current_datetime?: boolean;
};
export type AssistantUpdateParams = {
@ -113,6 +114,7 @@ export type AssistantUpdateParams = {
tools?: Array<FunctionTool | string>;
tool_resources?: ToolResources;
endpoint: AssistantsEndpoint;
append_current_datetime?: boolean;
};
export type AssistantListParams = {
@ -528,6 +530,7 @@ export type AssistantDocument = {
actions?: string[];
createdAt?: Date;
updatedAt?: Date;
append_current_datetime?: boolean;
};
/* Agent types */