| import { logger } from '@librechat/data-schemas'; |
| import { EModelEndpoint, EToolResources, AgentCapabilities } from 'librechat-data-provider'; |
| import type { AgentToolResources, TFile, AgentBaseResource } from 'librechat-data-provider'; |
| import type { IMongoFile, AppConfig, IUser } from '@librechat/data-schemas'; |
| import type { FilterQuery, QueryOptions, ProjectionType } from 'mongoose'; |
| import type { Request as ServerRequest } from 'express'; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| export type TGetFiles = ( |
| filter: FilterQuery<IMongoFile>, |
| _sortOptions: ProjectionType<IMongoFile> | null | undefined, |
| selectFields: QueryOptions<IMongoFile> | null | undefined, |
| options?: { userId?: string; agentId?: string }, |
| ) => Promise<Array<TFile>>; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const addFileToResource = ({ |
| file, |
| resourceType, |
| tool_resources, |
| processedResourceFiles, |
| }: { |
| file: TFile; |
| resourceType: EToolResources; |
| tool_resources: AgentToolResources; |
| processedResourceFiles: Set<string>; |
| }): void => { |
| if (!file.file_id) { |
| return; |
| } |
|
|
| const resourceKey = `${resourceType}:${file.file_id}`; |
| if (processedResourceFiles.has(resourceKey)) { |
| return; |
| } |
|
|
| const resource = tool_resources[resourceType as keyof AgentToolResources] ?? {}; |
| if (!resource.files) { |
| (tool_resources[resourceType as keyof AgentToolResources] as AgentBaseResource) = { |
| ...resource, |
| files: [], |
| }; |
| } |
|
|
| |
| const resourceFiles = tool_resources[resourceType as keyof AgentToolResources]?.files; |
| const alreadyExists = resourceFiles?.some((f: TFile) => f.file_id === file.file_id); |
|
|
| if (!alreadyExists) { |
| resourceFiles?.push(file); |
| processedResourceFiles.add(resourceKey); |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const categorizeFileForToolResources = ({ |
| file, |
| tool_resources, |
| requestFileSet, |
| processedResourceFiles, |
| }: { |
| file: TFile; |
| tool_resources: AgentToolResources; |
| requestFileSet: Set<string>; |
| processedResourceFiles: Set<string>; |
| }): void => { |
| if (file.metadata?.fileIdentifier) { |
| addFileToResource({ |
| file, |
| resourceType: EToolResources.execute_code, |
| tool_resources, |
| processedResourceFiles, |
| }); |
| return; |
| } |
|
|
| if (file.embedded === true) { |
| addFileToResource({ |
| file, |
| resourceType: EToolResources.file_search, |
| tool_resources, |
| processedResourceFiles, |
| }); |
| return; |
| } |
|
|
| if ( |
| requestFileSet.has(file.file_id) && |
| file.type.startsWith('image') && |
| file.height && |
| file.width |
| ) { |
| addFileToResource({ |
| file, |
| resourceType: EToolResources.image_edit, |
| tool_resources, |
| processedResourceFiles, |
| }); |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export const primeResources = async ({ |
| req, |
| appConfig, |
| getFiles, |
| requestFileSet, |
| attachments: _attachments, |
| tool_resources: _tool_resources, |
| agentId, |
| }: { |
| req: ServerRequest & { user?: IUser }; |
| appConfig: AppConfig; |
| requestFileSet: Set<string>; |
| attachments: Promise<Array<TFile | null>> | undefined; |
| tool_resources: AgentToolResources | undefined; |
| getFiles: TGetFiles; |
| agentId?: string; |
| }): Promise<{ |
| attachments: Array<TFile | undefined> | undefined; |
| tool_resources: AgentToolResources | undefined; |
| }> => { |
| try { |
| |
| |
| |
| |
| const attachments: Array<TFile> = []; |
| |
| |
| |
| |
| |
| const attachmentFileIds = new Set<string>(); |
| |
| |
| |
| |
| |
| const processedResourceFiles = new Set<string>(); |
| |
| |
| |
| |
| const tool_resources: AgentToolResources = { ...(_tool_resources ?? {}) }; |
|
|
| |
| for (const [resourceType, resource] of Object.entries(tool_resources)) { |
| if (!resource) { |
| continue; |
| } |
|
|
| |
| tool_resources[resourceType as keyof AgentToolResources] = { |
| ...resource, |
| |
| ...(resource.files && { files: [...resource.files] }), |
| ...(resource.file_ids && { file_ids: [...resource.file_ids] }), |
| ...(resource.vector_store_ids && { vector_store_ids: [...resource.vector_store_ids] }), |
| } as AgentBaseResource; |
|
|
| |
| if (resource.files && Array.isArray(resource.files)) { |
| for (const file of resource.files) { |
| if (file?.file_id) { |
| processedResourceFiles.add(`${resourceType}:${file.file_id}`); |
| |
| if (resourceType !== EToolResources.context && resourceType !== EToolResources.ocr) { |
| attachmentFileIds.add(file.file_id); |
| } |
| } |
| } |
| } |
| } |
|
|
| const isContextEnabled = ( |
| appConfig?.endpoints?.[EModelEndpoint.agents]?.capabilities ?? [] |
| ).includes(AgentCapabilities.context); |
|
|
| const fileIds = tool_resources[EToolResources.context]?.file_ids ?? []; |
| const ocrFileIds = tool_resources[EToolResources.ocr]?.file_ids; |
| if (ocrFileIds != null) { |
| fileIds.push(...ocrFileIds); |
| delete tool_resources[EToolResources.ocr]; |
| } |
|
|
| if (fileIds.length > 0 && isContextEnabled) { |
| delete tool_resources[EToolResources.context]; |
| const context = await getFiles( |
| { |
| file_id: { $in: fileIds }, |
| }, |
| {}, |
| {}, |
| { userId: req.user?.id, agentId }, |
| ); |
|
|
| for (const file of context) { |
| if (!file?.file_id) { |
| continue; |
| } |
|
|
| |
| attachmentFileIds.delete(file.file_id); |
|
|
| |
| attachments.push(file); |
| attachmentFileIds.add(file.file_id); |
|
|
| |
| categorizeFileForToolResources({ |
| file, |
| tool_resources, |
| requestFileSet, |
| processedResourceFiles, |
| }); |
| } |
| } |
|
|
| if (!_attachments) { |
| return { attachments: attachments.length > 0 ? attachments : undefined, tool_resources }; |
| } |
|
|
| const files = await _attachments; |
|
|
| for (const file of files) { |
| if (!file) { |
| continue; |
| } |
|
|
| categorizeFileForToolResources({ |
| file, |
| tool_resources, |
| requestFileSet, |
| processedResourceFiles, |
| }); |
|
|
| if (file.file_id && attachmentFileIds.has(file.file_id)) { |
| continue; |
| } |
|
|
| attachments.push(file); |
| if (file.file_id) { |
| attachmentFileIds.add(file.file_id); |
| } |
| } |
|
|
| return { attachments: attachments.length > 0 ? attachments : [], tool_resources }; |
| } catch (error) { |
| logger.error('Error priming resources', error); |
|
|
| |
| let safeAttachments: Array<TFile | undefined> = []; |
| if (_attachments) { |
| try { |
| const attachmentFiles = await _attachments; |
| safeAttachments = (attachmentFiles?.filter((file) => !!file) ?? []) as Array<TFile>; |
| } catch (attachmentError) { |
| |
| logger.error('Error resolving attachments in catch block', attachmentError); |
| safeAttachments = []; |
| } |
| } |
|
|
| return { |
| attachments: safeAttachments, |
| tool_resources: _tool_resources, |
| }; |
| } |
| }; |
|
|