| | import { |
| | OCRStrategy, |
| | FileSources, |
| | EModelEndpoint, |
| | EImageOutputType, |
| | AgentCapabilities, |
| | defaultSocialLogins, |
| | validateAzureGroups, |
| | defaultAgentCapabilities, |
| | } from 'librechat-data-provider'; |
| | import type { TCustomConfig } from 'librechat-data-provider'; |
| |
|
| | jest.mock('@librechat/data-schemas', () => ({ |
| | ...jest.requireActual('@librechat/data-schemas'), |
| | logger: { |
| | info: jest.fn(), |
| | warn: jest.fn(), |
| | error: jest.fn(), |
| | debug: jest.fn(), |
| | }, |
| | })); |
| |
|
| | import { AppService } from '@librechat/data-schemas'; |
| |
|
| | const azureGroups = [ |
| | { |
| | group: 'librechat-westus', |
| | apiKey: '${WESTUS_API_KEY}', |
| | instanceName: 'librechat-westus', |
| | version: '2023-12-01-preview', |
| | models: { |
| | 'gpt-4-vision-preview': { |
| | deploymentName: 'gpt-4-vision-preview', |
| | version: '2024-02-15-preview', |
| | }, |
| | 'gpt-3.5-turbo': { |
| | deploymentName: 'gpt-35-turbo', |
| | }, |
| | 'gpt-3.5-turbo-1106': { |
| | deploymentName: 'gpt-35-turbo-1106', |
| | }, |
| | 'gpt-4': { |
| | deploymentName: 'gpt-4', |
| | }, |
| | 'gpt-4-1106-preview': { |
| | deploymentName: 'gpt-4-1106-preview', |
| | }, |
| | }, |
| | }, |
| | { |
| | group: 'librechat-eastus', |
| | apiKey: '${EASTUS_API_KEY}', |
| | instanceName: 'librechat-eastus', |
| | deploymentName: 'gpt-4-turbo', |
| | version: '2024-02-15-preview', |
| | models: { |
| | 'gpt-4-turbo': true, |
| | }, |
| | } as const, |
| | ]; |
| |
|
| | describe('AppService', () => { |
| | const mockSystemTools = { |
| | ExampleTool: { |
| | type: 'function', |
| | function: { |
| | description: 'Example tool function', |
| | name: 'exampleFunction', |
| | parameters: { |
| | type: 'object', |
| | properties: { |
| | param1: { type: 'string', description: 'An example parameter' }, |
| | }, |
| | required: ['param1'], |
| | }, |
| | }, |
| | }, |
| | }; |
| |
|
| | beforeEach(() => { |
| | process.env.CDN_PROVIDER = undefined; |
| | jest.clearAllMocks(); |
| | }); |
| |
|
| | it('should correctly assign process.env and initialize app config based on custom config', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | registration: { socialLogins: ['testLogin'] }, |
| | fileStrategy: 'testStrategy' as FileSources, |
| | balance: { |
| | enabled: true, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config, systemTools: mockSystemTools }); |
| |
|
| | expect(process.env.CDN_PROVIDER).toEqual('testStrategy'); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | config: expect.objectContaining({ |
| | fileStrategy: 'testStrategy', |
| | }), |
| | registration: expect.objectContaining({ |
| | socialLogins: ['testLogin'], |
| | }), |
| | fileStrategy: 'testStrategy', |
| | interfaceConfig: expect.objectContaining({ |
| | endpointsMenu: true, |
| | modelSelect: true, |
| | parameters: true, |
| | sidePanel: true, |
| | presets: true, |
| | }), |
| | mcpConfig: null, |
| | imageOutputType: expect.any(String), |
| | fileConfig: undefined, |
| | secureImageLinks: undefined, |
| | balance: { enabled: true }, |
| | filteredTools: undefined, |
| | includedTools: undefined, |
| | webSearch: expect.objectContaining({ |
| | safeSearch: 1, |
| | jinaApiKey: '${JINA_API_KEY}', |
| | jinaApiUrl: '${JINA_API_URL}', |
| | cohereApiKey: '${COHERE_API_KEY}', |
| | serperApiKey: '${SERPER_API_KEY}', |
| | searxngApiKey: '${SEARXNG_API_KEY}', |
| | firecrawlApiKey: '${FIRECRAWL_API_KEY}', |
| | firecrawlApiUrl: '${FIRECRAWL_API_URL}', |
| | searxngInstanceUrl: '${SEARXNG_INSTANCE_URL}', |
| | }), |
| | memory: undefined, |
| | endpoints: expect.objectContaining({ |
| | agents: expect.objectContaining({ |
| | disableBuilder: false, |
| | capabilities: expect.arrayContaining([...defaultAgentCapabilities]), |
| | maxCitations: 30, |
| | maxCitationsPerFile: 7, |
| | minRelevanceScore: 0.45, |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should change the `imageOutputType` based on config value', async () => { |
| | const config = { |
| | version: '0.10.0', |
| | imageOutputType: EImageOutputType.WEBP, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | imageOutputType: EImageOutputType.WEBP, |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should default to `PNG` `imageOutputType` with no provided type', async () => { |
| | const config = { |
| | version: '0.10.0', |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | imageOutputType: EImageOutputType.PNG, |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should default to `PNG` `imageOutputType` with no provided config', async () => { |
| | const config = {}; |
| |
|
| | const result = await AppService({ config }); |
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | imageOutputType: EImageOutputType.PNG, |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should load and format tools accurately with defined structure', async () => { |
| | const config = {}; |
| |
|
| | const result = await AppService({ config, systemTools: mockSystemTools }); |
| |
|
| | |
| | expect(result.availableTools).toBeDefined(); |
| | expect(result.availableTools?.ExampleTool).toEqual({ |
| | type: 'function', |
| | function: { |
| | description: 'Example tool function', |
| | name: 'exampleFunction', |
| | parameters: { |
| | type: 'object', |
| | properties: { |
| | param1: { type: 'string', description: 'An example parameter' }, |
| | }, |
| | required: ['param1'], |
| | }, |
| | }, |
| | }); |
| | }); |
| |
|
| | it('should correctly configure Assistants endpoint based on custom config', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | endpoints: { |
| | [EModelEndpoint.assistants]: { |
| | disableBuilder: true, |
| | pollIntervalMs: 5000, |
| | timeoutMs: 30000, |
| | supportedIds: ['id1', 'id2'], |
| | privateAssistants: false, |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | [EModelEndpoint.assistants]: expect.objectContaining({ |
| | disableBuilder: true, |
| | pollIntervalMs: 5000, |
| | timeoutMs: 30000, |
| | supportedIds: expect.arrayContaining(['id1', 'id2']), |
| | privateAssistants: false, |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should correctly configure Agents endpoint based on custom config', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | endpoints: { |
| | [EModelEndpoint.agents]: { |
| | disableBuilder: true, |
| | recursionLimit: 10, |
| | maxRecursionLimit: 20, |
| | allowedProviders: ['openai', 'anthropic'], |
| | capabilities: [AgentCapabilities.tools, AgentCapabilities.actions], |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | [EModelEndpoint.agents]: expect.objectContaining({ |
| | disableBuilder: true, |
| | recursionLimit: 10, |
| | maxRecursionLimit: 20, |
| | allowedProviders: expect.arrayContaining(['openai', 'anthropic']), |
| | capabilities: expect.arrayContaining([ |
| | AgentCapabilities.tools, |
| | AgentCapabilities.actions, |
| | ]), |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should configure Agents endpoint with defaults when no config is provided', async () => { |
| | const config = {}; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | [EModelEndpoint.agents]: expect.objectContaining({ |
| | disableBuilder: false, |
| | capabilities: expect.arrayContaining([...defaultAgentCapabilities]), |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should configure Agents endpoint with defaults when endpoints exist but agents is not defined', async () => { |
| | const config = { |
| | endpoints: { |
| | [EModelEndpoint.openAI]: { |
| | titleConvo: true, |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | [EModelEndpoint.agents]: expect.objectContaining({ |
| | disableBuilder: false, |
| | capabilities: expect.arrayContaining([...defaultAgentCapabilities]), |
| | }), |
| | [EModelEndpoint.openAI]: expect.objectContaining({ |
| | titleConvo: true, |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should correctly configure minimum Azure OpenAI Assistant values', async () => { |
| | const assistantGroups = [azureGroups[0], { ...azureGroups[1], assistants: true }]; |
| | const config = { |
| | endpoints: { |
| | [EModelEndpoint.azureOpenAI]: { |
| | groups: assistantGroups, |
| | assistants: true, |
| | }, |
| | }, |
| | }; |
| |
|
| | process.env.WESTUS_API_KEY = 'westus-key'; |
| | process.env.EASTUS_API_KEY = 'eastus-key'; |
| |
|
| | const result = await AppService({ config }); |
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | [EModelEndpoint.azureAssistants]: expect.objectContaining({ |
| | capabilities: expect.arrayContaining([ |
| | expect.any(String), |
| | expect.any(String), |
| | expect.any(String), |
| | ]), |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should correctly configure Azure OpenAI endpoint based on custom config', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | endpoints: { |
| | [EModelEndpoint.azureOpenAI]: { |
| | groups: azureGroups, |
| | }, |
| | }, |
| | }; |
| |
|
| | process.env.WESTUS_API_KEY = 'westus-key'; |
| | process.env.EASTUS_API_KEY = 'eastus-key'; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | const { modelNames, modelGroupMap, groupMap } = validateAzureGroups(azureGroups); |
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | [EModelEndpoint.azureOpenAI]: expect.objectContaining({ |
| | modelNames, |
| | modelGroupMap, |
| | groupMap, |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should not modify FILE_UPLOAD environment variables without rate limits', async () => { |
| | |
| | process.env.FILE_UPLOAD_IP_MAX = '10'; |
| | process.env.FILE_UPLOAD_IP_WINDOW = '15'; |
| | process.env.FILE_UPLOAD_USER_MAX = '5'; |
| | process.env.FILE_UPLOAD_USER_WINDOW = '20'; |
| |
|
| | const initialEnv = { ...process.env }; |
| | const config = {}; |
| |
|
| | await AppService({ config }); |
| |
|
| | |
| | expect(process.env.FILE_UPLOAD_IP_MAX).toEqual(initialEnv.FILE_UPLOAD_IP_MAX); |
| | expect(process.env.FILE_UPLOAD_IP_WINDOW).toEqual(initialEnv.FILE_UPLOAD_IP_WINDOW); |
| | expect(process.env.FILE_UPLOAD_USER_MAX).toEqual(initialEnv.FILE_UPLOAD_USER_MAX); |
| | expect(process.env.FILE_UPLOAD_USER_WINDOW).toEqual(initialEnv.FILE_UPLOAD_USER_WINDOW); |
| | }); |
| |
|
| | it('should fallback to default FILE_UPLOAD environment variables when rate limits are unspecified', async () => { |
| | |
| | process.env.FILE_UPLOAD_IP_MAX = 'initialMax'; |
| | process.env.FILE_UPLOAD_IP_WINDOW = 'initialWindow'; |
| | process.env.FILE_UPLOAD_USER_MAX = 'initialUserMax'; |
| | process.env.FILE_UPLOAD_USER_WINDOW = 'initialUserWindow'; |
| | const config = {}; |
| |
|
| | await AppService({ config }); |
| |
|
| | |
| | expect(process.env.FILE_UPLOAD_IP_MAX).toEqual('initialMax'); |
| | expect(process.env.FILE_UPLOAD_IP_WINDOW).toEqual('initialWindow'); |
| | expect(process.env.FILE_UPLOAD_USER_MAX).toEqual('initialUserMax'); |
| | expect(process.env.FILE_UPLOAD_USER_WINDOW).toEqual('initialUserWindow'); |
| | }); |
| |
|
| | it('should not modify IMPORT environment variables without rate limits', async () => { |
| | |
| | process.env.IMPORT_IP_MAX = '10'; |
| | process.env.IMPORT_IP_WINDOW = '15'; |
| | process.env.IMPORT_USER_MAX = '5'; |
| | process.env.IMPORT_USER_WINDOW = '20'; |
| |
|
| | const initialEnv = { ...process.env }; |
| | const config = {}; |
| |
|
| | await AppService({ config }); |
| |
|
| | |
| | expect(process.env.IMPORT_IP_MAX).toEqual(initialEnv.IMPORT_IP_MAX); |
| | expect(process.env.IMPORT_IP_WINDOW).toEqual(initialEnv.IMPORT_IP_WINDOW); |
| | expect(process.env.IMPORT_USER_MAX).toEqual(initialEnv.IMPORT_USER_MAX); |
| | expect(process.env.IMPORT_USER_WINDOW).toEqual(initialEnv.IMPORT_USER_WINDOW); |
| | }); |
| |
|
| | it('should fallback to default IMPORT environment variables when rate limits are unspecified', async () => { |
| | |
| | process.env.IMPORT_IP_MAX = 'initialMax'; |
| | process.env.IMPORT_IP_WINDOW = 'initialWindow'; |
| | process.env.IMPORT_USER_MAX = 'initialUserMax'; |
| | process.env.IMPORT_USER_WINDOW = 'initialUserWindow'; |
| | const config = {}; |
| |
|
| | await AppService({ config }); |
| |
|
| | |
| | expect(process.env.IMPORT_IP_MAX).toEqual('initialMax'); |
| | expect(process.env.IMPORT_IP_WINDOW).toEqual('initialWindow'); |
| | expect(process.env.IMPORT_USER_MAX).toEqual('initialUserMax'); |
| | expect(process.env.IMPORT_USER_WINDOW).toEqual('initialUserWindow'); |
| | }); |
| |
|
| | it('should correctly configure endpoint with titlePrompt, titleMethod, and titlePromptTemplate', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | endpoints: { |
| | [EModelEndpoint.openAI]: { |
| | titleConvo: true, |
| | titleModel: 'gpt-3.5-turbo', |
| | titleMethod: 'structured', |
| | titlePrompt: 'Custom title prompt for conversation', |
| | titlePromptTemplate: 'Summarize this conversation: {{conversation}}', |
| | }, |
| | [EModelEndpoint.assistants]: { |
| | titleMethod: 'functions', |
| | titlePrompt: 'Generate a title for this assistant conversation', |
| | titlePromptTemplate: 'Assistant conversation template: {{messages}}', |
| | }, |
| | [EModelEndpoint.azureOpenAI]: { |
| | groups: azureGroups, |
| | titleConvo: true, |
| | titleMethod: 'completion', |
| | titleModel: 'gpt-4', |
| | titlePrompt: 'Azure title prompt', |
| | titlePromptTemplate: 'Azure conversation: {{context}}', |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | |
| | [EModelEndpoint.openAI]: expect.objectContaining({ |
| | titleConvo: true, |
| | titleModel: 'gpt-3.5-turbo', |
| | titleMethod: 'structured', |
| | titlePrompt: 'Custom title prompt for conversation', |
| | titlePromptTemplate: 'Summarize this conversation: {{conversation}}', |
| | }), |
| | |
| | [EModelEndpoint.assistants]: expect.objectContaining({ |
| | titleMethod: 'functions', |
| | titlePrompt: 'Generate a title for this assistant conversation', |
| | titlePromptTemplate: 'Assistant conversation template: {{messages}}', |
| | }), |
| | |
| | [EModelEndpoint.azureOpenAI]: expect.objectContaining({ |
| | titleConvo: true, |
| | titleMethod: 'completion', |
| | titleModel: 'gpt-4', |
| | titlePrompt: 'Azure title prompt', |
| | titlePromptTemplate: 'Azure conversation: {{context}}', |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should configure Agent endpoint with title generation settings', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | endpoints: { |
| | [EModelEndpoint.agents]: { |
| | disableBuilder: false, |
| | titleConvo: true, |
| | titleModel: 'gpt-4', |
| | titleMethod: 'structured', |
| | titlePrompt: 'Generate a descriptive title for this agent conversation', |
| | titlePromptTemplate: 'Agent conversation summary: {{content}}', |
| | recursionLimit: 15, |
| | capabilities: [AgentCapabilities.tools, AgentCapabilities.actions], |
| | maxCitations: 30, |
| | maxCitationsPerFile: 7, |
| | minRelevanceScore: 0.45, |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | [EModelEndpoint.agents]: expect.objectContaining({ |
| | disableBuilder: false, |
| | titleConvo: true, |
| | titleModel: 'gpt-4', |
| | titleMethod: 'structured', |
| | titlePrompt: 'Generate a descriptive title for this agent conversation', |
| | titlePromptTemplate: 'Agent conversation summary: {{content}}', |
| | recursionLimit: 15, |
| | capabilities: expect.arrayContaining([ |
| | AgentCapabilities.tools, |
| | AgentCapabilities.actions, |
| | ]), |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should handle missing title configuration options with defaults', async () => { |
| | const config = { |
| | endpoints: { |
| | [EModelEndpoint.openAI]: { |
| | titleConvo: true, |
| | |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | [EModelEndpoint.openAI]: expect.objectContaining({ |
| | titleConvo: true, |
| | }), |
| | }), |
| | }), |
| | ); |
| |
|
| | |
| | expect(result.endpoints[EModelEndpoint.openAI].titlePrompt).toBeUndefined(); |
| | expect(result.endpoints[EModelEndpoint.openAI].titlePromptTemplate).toBeUndefined(); |
| | expect(result.endpoints[EModelEndpoint.openAI].titleMethod).toBeUndefined(); |
| | }); |
| |
|
| | it('should correctly configure titleEndpoint when specified', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | endpoints: { |
| | [EModelEndpoint.openAI]: { |
| | titleConvo: true, |
| | titleModel: 'gpt-3.5-turbo', |
| | titleEndpoint: EModelEndpoint.anthropic, |
| | titlePrompt: 'Generate a concise title', |
| | }, |
| | [EModelEndpoint.agents]: { |
| | disableBuilder: false, |
| | capabilities: [AgentCapabilities.tools], |
| | maxCitations: 30, |
| | maxCitationsPerFile: 7, |
| | minRelevanceScore: 0.45, |
| | titleEndpoint: 'custom-provider', |
| | titleMethod: 'structured', |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | |
| | [EModelEndpoint.openAI]: expect.objectContaining({ |
| | titleConvo: true, |
| | titleModel: 'gpt-3.5-turbo', |
| | titleEndpoint: EModelEndpoint.anthropic, |
| | titlePrompt: 'Generate a concise title', |
| | }), |
| | |
| | [EModelEndpoint.agents]: expect.objectContaining({ |
| | titleEndpoint: 'custom-provider', |
| | titleMethod: 'structured', |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should correctly configure all endpoint when specified', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | endpoints: { |
| | all: { |
| | titleConvo: true, |
| | titleModel: 'gpt-4o-mini', |
| | titleMethod: 'structured', |
| | titlePrompt: 'Default title prompt for all endpoints', |
| | titlePromptTemplate: 'Default template: {{conversation}}', |
| | titleEndpoint: EModelEndpoint.anthropic, |
| | streamRate: 50, |
| | }, |
| | [EModelEndpoint.openAI]: { |
| | titleConvo: true, |
| | titleModel: 'gpt-3.5-turbo', |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | |
| | endpoints: expect.objectContaining({ |
| | all: expect.objectContaining({ |
| | titleConvo: true, |
| | titleModel: 'gpt-4o-mini', |
| | titleMethod: 'structured', |
| | titlePrompt: 'Default title prompt for all endpoints', |
| | titlePromptTemplate: 'Default template: {{conversation}}', |
| | titleEndpoint: EModelEndpoint.anthropic, |
| | streamRate: 50, |
| | }), |
| | |
| | [EModelEndpoint.openAI]: expect.objectContaining({ |
| | titleConvo: true, |
| | titleModel: 'gpt-3.5-turbo', |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| | }); |
| |
|
| | describe('AppService updating app config and issuing warnings', () => { |
| | let initialEnv: NodeJS.ProcessEnv; |
| |
|
| | beforeEach(() => { |
| | |
| | initialEnv = { ...process.env }; |
| |
|
| | process.env.CDN_PROVIDER = undefined; |
| | jest.clearAllMocks(); |
| | }); |
| |
|
| | afterEach(() => { |
| | |
| | process.env = { ...initialEnv }; |
| | }); |
| |
|
| | it('should initialize app config with default values if config is empty', async () => { |
| | const config = {}; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | config: {}, |
| | fileStrategy: FileSources.local, |
| | registration: expect.objectContaining({ |
| | socialLogins: defaultSocialLogins, |
| | }), |
| | balance: expect.objectContaining({ |
| | enabled: false, |
| | startBalance: undefined, |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should initialize app config with values from config', async () => { |
| | |
| | const config: Partial<TCustomConfig> = { |
| | fileStrategy: FileSources.firebase, |
| | registration: { socialLogins: ['testLogin'] }, |
| | balance: { |
| | enabled: false, |
| | startBalance: 5000, |
| | autoRefillEnabled: true, |
| | refillIntervalValue: 15, |
| | refillIntervalUnit: 'hours', |
| | refillAmount: 5000, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | config, |
| | fileStrategy: config.fileStrategy, |
| | registration: expect.objectContaining({ |
| | socialLogins: config.registration?.socialLogins, |
| | }), |
| | balance: config.balance, |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should apply the assistants endpoint configuration correctly to app config', async () => { |
| | const config: Partial<TCustomConfig> = { |
| | endpoints: { |
| | assistants: { |
| | version: 'v2', |
| | retrievalModels: ['gpt-4', 'gpt-3.5-turbo'], |
| | capabilities: [], |
| | disableBuilder: true, |
| | pollIntervalMs: 5000, |
| | timeoutMs: 30000, |
| | supportedIds: ['id1', 'id2'], |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | endpoints: expect.objectContaining({ |
| | assistants: expect.objectContaining({ |
| | disableBuilder: true, |
| | pollIntervalMs: 5000, |
| | timeoutMs: 30000, |
| | supportedIds: ['id1', 'id2'], |
| | }), |
| | }), |
| | }), |
| | ); |
| |
|
| | |
| | expect(result.endpoints.assistants.excludedIds).toBeUndefined(); |
| | }); |
| |
|
| | it('should not parse environment variable references in OCR config', async () => { |
| | |
| | const config: Partial<TCustomConfig> = { |
| | ocr: { |
| | apiKey: '${OCR_API_KEY_CUSTOM_VAR_NAME}', |
| | baseURL: '${OCR_BASEURL_CUSTOM_VAR_NAME}', |
| | strategy: OCRStrategy.MISTRAL_OCR, |
| | mistralModel: 'mistral-medium', |
| | }, |
| | }; |
| |
|
| | |
| | process.env.OCR_API_KEY_CUSTOM_VAR_NAME = 'actual-api-key'; |
| | process.env.OCR_BASEURL_CUSTOM_VAR_NAME = 'https://actual-ocr-url.com'; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | |
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | ocr: expect.objectContaining({ |
| | apiKey: '${OCR_API_KEY_CUSTOM_VAR_NAME}', |
| | baseURL: '${OCR_BASEURL_CUSTOM_VAR_NAME}', |
| | strategy: 'mistral_ocr', |
| | mistralModel: 'mistral-medium', |
| | }), |
| | }), |
| | ); |
| | }); |
| |
|
| | it('should correctly configure peoplePicker permissions when specified', async () => { |
| | const config = { |
| | interface: { |
| | peoplePicker: { |
| | users: true, |
| | groups: true, |
| | roles: true, |
| | }, |
| | }, |
| | }; |
| |
|
| | const result = await AppService({ config }); |
| |
|
| | |
| | expect(result).toEqual( |
| | expect.objectContaining({ |
| | interfaceConfig: expect.objectContaining({ |
| | peoplePicker: expect.objectContaining({ |
| | users: true, |
| | groups: true, |
| | roles: true, |
| | }), |
| | }), |
| | }), |
| | ); |
| | }); |
| | }); |
| |
|