LibreChat/packages/data-provider/src/file-config.spec.ts
Danny Avila 2524d33362
📂 refactor: Cleanup File Filtering Logic, Improve Validation (#10414)
* feat: add filterFilesByEndpointConfig to filter disabled file processing by provider

* chore: explicit define of endpointFileConfig for better debugging

* refactor: move `normalizeEndpointName` to data-provider as used app-wide

* chore: remove overrideEndpoint from useFileHandling

* refactor: improve endpoint file config selection

* refactor: update filterFilesByEndpointConfig to accept structured parameters and improve endpoint file config handling

* refactor: replace defaultFileConfig with getEndpointFileConfig for improved file configuration handling across components

* test: add comprehensive unit tests for getEndpointFileConfig to validate endpoint configuration handling

* refactor: streamline agent endpoint assignment and improve file filtering logic

* feat: add error handling for disabled file uploads in endpoint configuration

* refactor: update encodeAndFormat functions to accept structured parameters for provider and endpoint

* refactor: streamline requestFiles handling in initializeAgent function

* fix: getEndpointFileConfig partial config merging scenarios

* refactor: enhance mergeWithDefault function to support document-supported providers with comprehensive MIME types

* refactor: user-configured default file config in getEndpointFileConfig

* fix: prevent file handling when endpoint is disabled and file is dragged to chat

* refactor: move `getEndpointField` to `data-provider` and update usage across components and hooks

* fix: prioritize endpointType based on agent.endpoint in file filtering logic

* fix: prioritize agent.endpoint in file filtering logic and remove unnecessary endpointType defaulting
2025-11-10 19:05:30 -05:00

1097 lines
30 KiB
TypeScript

import type { FileConfig } from './types/files';
import {
fileConfig as baseFileConfig,
getEndpointFileConfig,
mergeFileConfig,
} from './file-config';
import { EModelEndpoint } from './schemas';
describe('getEndpointFileConfig', () => {
describe('custom endpoint lookup', () => {
it('should find custom endpoint by direct lookup', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
ollama: {
disabled: true,
fileLimit: 5,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'ollama',
endpointType: EModelEndpoint.custom,
});
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(5);
});
it('should find custom endpoint by normalized lookup', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
ollama: {
disabled: true,
fileLimit: 7,
},
},
};
/** Test with non-normalized name */
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'Ollama',
endpointType: EModelEndpoint.custom,
});
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(7);
});
it('should fallback to generic custom config when specific endpoint not found', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.custom]: {
disabled: false,
fileLimit: 3,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'unknownCustomEndpoint',
endpointType: EModelEndpoint.custom,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(3);
});
it('should fallback to agents config when custom and specific endpoint not found', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.agents]: {
disabled: false,
fileLimit: 8,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'unknownCustomEndpoint',
endpointType: EModelEndpoint.custom,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(8);
});
it('should use base agents config when only default is dynamically configured', () => {
const dynamicConfig = {
endpoints: {
default: {
disabled: false,
fileLimit: 12,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: 'unknownCustomEndpoint',
endpointType: EModelEndpoint.custom,
});
/**
* Should use base agents config (fileLimit: 10) since it exists in baseFileConfig
* and custom endpoints fall back to agents
*/
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(10); /** From baseFileConfig.endpoints.agents */
});
it('should prioritize specific custom endpoint over generic custom config', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.custom]: {
disabled: false,
fileLimit: 20,
},
ollama: {
disabled: true,
fileLimit: 3,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'ollama',
endpointType: EModelEndpoint.custom,
});
/** Should use ollama config, not generic custom */
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(3);
});
it('should skip standard endpoint keys in normalized lookup', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
default: {
disabled: false,
fileLimit: 99,
},
},
};
/** "default" should not match via normalized lookup for custom endpoints */
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'default',
endpointType: EModelEndpoint.custom,
});
/** Should not use direct lookup, should fall back to default */
expect(result.fileLimit).toBe(99);
});
it('should handle complete fallback chain: specific -> custom -> agents -> default', () => {
const customConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
myOllama: {
disabled: true,
fileLimit: 1,
},
[EModelEndpoint.custom]: {
disabled: false,
fileLimit: 2,
},
[EModelEndpoint.agents]: {
disabled: false,
fileLimit: 3,
},
default: {
disabled: false,
fileLimit: 4,
},
},
};
/** 1. Should find specific config */
const specific = getEndpointFileConfig({
fileConfig: customConfig,
endpoint: 'myOllama',
endpointType: EModelEndpoint.custom,
});
expect(specific.fileLimit).toBe(1);
/** 2. Should fallback to custom when specific not found */
const customOnlyConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.custom]: {
disabled: false,
fileLimit: 2,
},
[EModelEndpoint.agents]: {
disabled: false,
fileLimit: 3,
},
default: {
disabled: false,
fileLimit: 4,
},
},
};
const customFallback = getEndpointFileConfig({
fileConfig: customOnlyConfig,
endpoint: 'unknownCustom',
endpointType: EModelEndpoint.custom,
});
expect(customFallback.fileLimit).toBe(2);
/** 3. Should fallback to agents */
const agentsFallback = getEndpointFileConfig({
fileConfig: {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.agents]: {
disabled: false,
fileLimit: 3,
},
default: {
disabled: false,
fileLimit: 4,
},
},
},
endpoint: 'unknownCustom',
endpointType: EModelEndpoint.custom,
});
expect(agentsFallback.fileLimit).toBe(3);
/**
* 4. Should use agents even if disabled (caller decides based on disabled flag)
* getEndpointFileConfig returns the config, doesn't filter based on disabled
*/
const agentsDisabledConfig = mergeFileConfig({
endpoints: {
[EModelEndpoint.agents]: {
disabled: true,
},
default: {
disabled: false,
fileLimit: 4,
},
},
});
const agentsDisabled = getEndpointFileConfig({
fileConfig: agentsDisabledConfig,
endpoint: 'unknownCustom',
endpointType: EModelEndpoint.custom,
});
/** Should return agents config (disabled: true), not skip to default */
expect(agentsDisabled.disabled).toBe(true);
expect(agentsDisabled.fileLimit).toBe(0); /** disabled: true sets fileLimit to 0 */
});
});
describe('standard endpoint lookup', () => {
it('should find endpoint by endpointType', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.openAI]: {
disabled: true,
fileLimit: 15,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'someOtherName',
endpointType: EModelEndpoint.openAI,
});
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(15);
});
it('should find endpoint by direct endpoint name', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.anthropic]: {
disabled: false,
fileLimit: 25,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.anthropic,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(25);
});
it('should find endpoint by normalized name', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
ollama: {
disabled: true,
fileLimit: 6,
},
},
};
/** Test normalization (Ollama -> ollama) */
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'Ollama',
});
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(6);
});
it('should use agents fallback for explicitly agents endpoint', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.agents]: {
disabled: false,
fileLimit: 11,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.agents,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(11);
});
it('should use agents fallback for unconfigured non-standard endpoint', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.agents]: {
disabled: false,
fileLimit: 10,
},
default: {
disabled: false,
fileLimit: 100,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'unconfiguredEndpoint',
});
/**
* With new logic, unconfigured endpoints are treated as custom
* and fall back through: specific -> custom -> agents -> default
* So this should use agents (fileLimit: 10), not default
*/
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(10);
});
it('should prioritize endpointType over endpoint name', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.openAI]: {
disabled: true,
fileLimit: 5,
},
[EModelEndpoint.anthropic]: {
disabled: false,
fileLimit: 10,
},
},
};
/** endpointType should take priority */
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.openAI,
endpointType: EModelEndpoint.anthropic,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(10);
});
});
describe('edge cases', () => {
it('should return default when fileConfig is null', () => {
const result = getEndpointFileConfig({
fileConfig: null,
endpoint: EModelEndpoint.openAI,
});
expect(result).toBeDefined();
expect(result.disabled).toBe(false);
});
it('should return default when fileConfig is undefined', () => {
const result = getEndpointFileConfig({
fileConfig: undefined,
endpoint: EModelEndpoint.openAI,
});
expect(result).toBeDefined();
expect(result.disabled).toBe(false);
});
it('should handle empty endpoint gracefully', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
default: {
disabled: false,
fileLimit: 50,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: '',
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(50);
});
it('should handle null endpoint gracefully', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
default: {
disabled: false,
fileLimit: 50,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: null,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(50);
});
it('should handle undefined endpoint gracefully', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
default: {
disabled: false,
fileLimit: 50,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: undefined,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(50);
});
it('should not mutate the input fileConfig', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.openAI]: {
disabled: false,
fileLimit: 10,
},
},
};
const originalDisabled = fileConfig.endpoints[EModelEndpoint.openAI]!.disabled;
getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.openAI,
});
/** Config should not be mutated */
expect(fileConfig.endpoints[EModelEndpoint.openAI]!.disabled).toBe(originalDisabled);
});
});
describe('assistants endpoint handling', () => {
it('should find assistants endpoint config', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.assistants]: {
disabled: false,
fileLimit: 20,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.assistants,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(20);
});
it('should find azureAssistants endpoint config', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.azureAssistants]: {
disabled: true,
fileLimit: 15,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.azureAssistants,
});
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(15);
});
it('should not fallback to agents for assistants endpoints', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.agents]: {
disabled: true,
fileLimit: 5,
},
default: {
disabled: false,
fileLimit: 10,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'unknownAssistants',
endpointType: EModelEndpoint.assistants,
});
/** Should use default, not agents */
expect(result.fileLimit).toBe(10);
});
});
describe('agents endpoint handling', () => {
it('should find agents endpoint config', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.agents]: {
disabled: false,
fileLimit: 9,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.agents,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(9);
});
});
describe('mergeFileConfig integration', () => {
it('should work with mergeFileConfig output for disabled endpoint', () => {
const dynamicConfig = {
endpoints: {
[EModelEndpoint.openAI]: {
disabled: true,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.openAI,
});
expect(result.disabled).toBe(true);
/** When disabled: true, merge sets these to 0 */
expect(result.fileLimit).toBe(0);
expect(result.fileSizeLimit).toBe(0);
expect(result.totalSizeLimit).toBe(0);
expect(result.supportedMimeTypes).toEqual([]);
});
it('should work with mergeFileConfig output for enabled endpoint', () => {
const dynamicConfig = {
endpoints: {
[EModelEndpoint.anthropic]: {
disabled: false,
fileLimit: 5,
fileSizeLimit: 10,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.anthropic,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(5);
/** Should convert MB to bytes */
expect(result.fileSizeLimit).toBe(10 * 1024 * 1024);
});
it('should preserve disabled: false in merged config', () => {
const dynamicConfig = {
endpoints: {
[EModelEndpoint.anthropic]: {
disabled: false,
fileLimit: 8,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.anthropic,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(8);
});
it('should not mutate base fileConfig during merge', () => {
const originalBaseAgentsConfig = { ...baseFileConfig.endpoints.agents };
const dynamicConfig = {
endpoints: {
[EModelEndpoint.agents]: {
disabled: true,
fileLimit: 1,
},
},
};
mergeFileConfig(dynamicConfig);
/** Base config should not be mutated */
expect(baseFileConfig.endpoints.agents).toEqual(originalBaseAgentsConfig);
});
});
describe('lookup priority verification', () => {
it('should check endpointType before endpoint for standard endpoints', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.openAI]: {
disabled: true,
fileLimit: 1,
},
wrongEndpoint: {
disabled: false,
fileLimit: 99,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'wrongEndpoint',
endpointType: EModelEndpoint.openAI,
});
/** Should use endpointType config, not endpoint */
expect(result.fileLimit).toBe(1);
});
it('should check endpoint when endpointType not found', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
myCustomEndpoint: {
disabled: true,
fileLimit: 7,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: 'myCustomEndpoint',
endpointType: 'notFound',
});
expect(result.fileLimit).toBe(7);
});
});
describe('disabled handling', () => {
it('should properly handle disabled: true', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.openAI]: {
disabled: true,
fileLimit: 0,
fileSizeLimit: 0,
totalSizeLimit: 0,
supportedMimeTypes: [],
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.openAI,
});
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(0);
expect(result.fileSizeLimit).toBe(0);
expect(result.totalSizeLimit).toBe(0);
expect(result.supportedMimeTypes).toEqual([]);
});
it('should properly handle disabled: false', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.anthropic]: {
disabled: false,
fileLimit: 10,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.anthropic,
});
expect(result.disabled).toBe(false);
expect(result.fileLimit).toBe(10);
});
it('should treat undefined disabled as enabled', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.google]: {
fileLimit: 10,
},
},
};
const result = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.google,
});
/** disabled should not be explicitly true */
expect(result.disabled).not.toBe(true);
});
});
describe('partial config merging', () => {
it('should merge partial endpoint config with default config', () => {
const dynamicConfig = {
endpoints: {
google: {
fileSizeLimit: 500,
/** Note: supportedMimeTypes not configured */
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.google,
});
/** Should have the configured fileSizeLimit */
expect(result.fileSizeLimit).toBe(500 * 1024 * 1024);
/** Should have supportedMimeTypes from default config */
expect(result.supportedMimeTypes).toBeDefined();
expect(Array.isArray(result.supportedMimeTypes)).toBe(true);
expect(result.supportedMimeTypes!.length).toBeGreaterThan(0);
/** Should have other fields from default */
expect(result.fileLimit).toBeDefined();
expect(result.totalSizeLimit).toBeDefined();
});
it('should not override explicitly set fields with default', () => {
const dynamicConfig = {
endpoints: {
anthropic: {
disabled: true,
fileLimit: 3,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.anthropic,
});
/** Should keep explicitly configured values */
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(0); /** disabled: true sets to 0 in merge */
/** But still get supportedMimeTypes from... wait, disabled: true clears this */
expect(result.supportedMimeTypes).toEqual([]);
});
it('should handle endpoint with only fileSizeLimit configured', () => {
const dynamicConfig = {
endpoints: {
openAI: {
fileSizeLimit: 100,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.openAI,
});
expect(result.fileSizeLimit).toBe(100 * 1024 * 1024);
/** Should get these from default */
expect(result.supportedMimeTypes).toBeDefined();
expect(result.fileLimit).toBeDefined();
expect(result.disabled).not.toBe(true);
});
it('should merge supportedMimeTypes from default when only fileSizeLimit is configured', () => {
/** This tests the exact scenario from the issue */
const dynamicConfig = {
endpoints: {
google: {
fileSizeLimit: 1000000024,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.google,
});
/** Should have the massive fileSizeLimit configured */
expect(result.fileSizeLimit).toBe(1000000024 * 1024 * 1024);
/** CRITICAL: Should have supportedMimeTypes from default, not undefined or [] */
expect(result.supportedMimeTypes).toBeDefined();
expect(Array.isArray(result.supportedMimeTypes)).toBe(true);
expect(result.supportedMimeTypes!.length).toBeGreaterThan(0);
/** Should have other default fields */
expect(result.fileLimit).toBe(10);
expect(result.disabled).toBe(false);
});
});
describe('real-world scenarios', () => {
it('should handle multi-provider custom endpoint configuration', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
ollama: {
disabled: false,
fileLimit: 5,
},
lmstudio: {
disabled: true,
fileLimit: 3,
},
[EModelEndpoint.custom]: {
disabled: false,
fileLimit: 10,
},
},
};
const ollamaResult = getEndpointFileConfig({
fileConfig,
endpoint: 'ollama',
endpointType: EModelEndpoint.custom,
});
expect(ollamaResult.fileLimit).toBe(5);
const lmstudioResult = getEndpointFileConfig({
fileConfig,
endpoint: 'lmstudio',
endpointType: EModelEndpoint.custom,
});
expect(lmstudioResult.disabled).toBe(true);
expect(lmstudioResult.fileLimit).toBe(3);
const unknownResult = getEndpointFileConfig({
fileConfig,
endpoint: 'unknownProvider',
endpointType: EModelEndpoint.custom,
});
expect(unknownResult.fileLimit).toBe(10);
});
it('should handle switching between endpoints correctly', () => {
const fileConfig: FileConfig = {
...baseFileConfig,
endpoints: {
...baseFileConfig.endpoints,
[EModelEndpoint.openAI]: {
disabled: true,
},
[EModelEndpoint.anthropic]: {
disabled: false,
fileLimit: 15,
},
},
};
const openaiResult = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.openAI,
});
expect(openaiResult.disabled).toBe(true);
const anthropicResult = getEndpointFileConfig({
fileConfig,
endpoint: EModelEndpoint.anthropic,
});
expect(anthropicResult.disabled).toBe(false);
expect(anthropicResult.fileLimit).toBe(15);
});
});
describe('user-configured default behavior', () => {
it('should use user-configured default as effective default when endpoint not found', () => {
const dynamicConfig = {
endpoints: {
default: {
fileLimit: 7,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.google,
});
expect(result.fileLimit).toBe(7);
expect(result.disabled).toBe(false);
expect(result.supportedMimeTypes).toBeDefined();
expect(Array.isArray(result.supportedMimeTypes)).toBe(true);
expect(result.supportedMimeTypes!.length).toBeGreaterThan(0);
});
it('should merge endpoint config against user default (not base default)', () => {
const dynamicConfig = {
endpoints: {
default: {
fileLimit: 7,
},
google: {
fileSizeLimit: 123,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.google,
});
/** fileLimit should come from user default */
expect(result.fileLimit).toBe(7);
/** fileSizeLimit should come from endpoint (converted to bytes) */
expect(result.fileSizeLimit).toBe(123 * 1024 * 1024);
});
it('should respect user-configured default supportedMimeTypes override', () => {
const dynamicConfig = {
endpoints: {
default: {
supportedMimeTypes: ['^text\\/plain$'],
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
});
/** Only text/plain should be allowed */
expect(result.supportedMimeTypes).toBeDefined();
expect(result.supportedMimeTypes!.length).toBe(1);
const [onlyRegex] = result.supportedMimeTypes as RegExp[];
expect(onlyRegex.test('text/plain')).toBe(true);
expect(onlyRegex.test('image/png')).toBe(false);
});
it('should propagate disabled from user default across fallbacks', () => {
const dynamicConfig = {
endpoints: {
default: {
disabled: true,
},
},
};
const merged = mergeFileConfig(dynamicConfig);
const result = getEndpointFileConfig({
fileConfig: merged,
endpoint: EModelEndpoint.google,
});
expect(result.disabled).toBe(true);
expect(result.fileLimit).toBe(0);
expect(result.fileSizeLimit).toBe(0);
expect(result.totalSizeLimit).toBe(0);
expect(result.supportedMimeTypes).toEqual([]);
});
});
});