mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-15 06:58:10 +01:00
Merge branch 'dev' into feat/prompt-enhancement
This commit is contained in:
commit
3d261a969d
365 changed files with 23826 additions and 8790 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "librechat-data-provider",
|
||||
"version": "0.7.86",
|
||||
"version": "0.7.88",
|
||||
"description": "data services for librechat apps",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.es.js",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import axios from 'axios';
|
||||
import { z } from 'zod';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
import axios from 'axios';
|
||||
import type { OpenAPIV3 } from 'openapi-types';
|
||||
import type { ParametersSchema } from '../src/actions';
|
||||
import type { FlowchartSchema } from './openapiSpecs';
|
||||
import {
|
||||
createURL,
|
||||
resolveRef,
|
||||
|
|
@ -15,9 +17,7 @@ import {
|
|||
scholarAIOpenapiSpec,
|
||||
swapidev,
|
||||
} from './openapiSpecs';
|
||||
import { AuthorizationTypeEnum, AuthTypeEnum } from '../src/types/assistants';
|
||||
import type { FlowchartSchema } from './openapiSpecs';
|
||||
import type { ParametersSchema } from '../src/actions';
|
||||
import { AuthorizationTypeEnum, AuthTypeEnum } from '../src/types/agents';
|
||||
|
||||
jest.mock('axios');
|
||||
const mockedAxios = axios as jest.Mocked<typeof axios>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,28 @@
|
|||
import { StdioOptionsSchema, StreamableHTTPOptionsSchema, processMCPEnv, MCPOptions } from '../src/mcp';
|
||||
import type { TUser } from 'librechat-data-provider';
|
||||
import {
|
||||
StreamableHTTPOptionsSchema,
|
||||
StdioOptionsSchema,
|
||||
processMCPEnv,
|
||||
MCPOptions,
|
||||
} from '../src/mcp';
|
||||
|
||||
// Helper function to create test user objects
|
||||
function createTestUser(
|
||||
overrides: Partial<TUser> & Record<string, unknown> = {},
|
||||
): TUser & Record<string, unknown> {
|
||||
return {
|
||||
id: 'test-user-id',
|
||||
username: 'testuser',
|
||||
email: 'test@example.com',
|
||||
name: 'Test User',
|
||||
avatar: 'https://example.com/avatar.png',
|
||||
provider: 'email',
|
||||
role: 'user',
|
||||
createdAt: new Date('2021-01-01').toISOString(),
|
||||
updatedAt: new Date('2021-01-01').toISOString(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Environment Variable Extraction (MCP)', () => {
|
||||
const originalEnv = process.env;
|
||||
|
|
@ -91,13 +115,13 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
|
||||
// Type is now required, so parsing should fail
|
||||
expect(() => StreamableHTTPOptionsSchema.parse(options)).toThrow();
|
||||
|
||||
|
||||
// With type provided, it should pass
|
||||
const validOptions = {
|
||||
type: 'streamable-http' as const,
|
||||
url: 'https://example.com/api',
|
||||
};
|
||||
|
||||
|
||||
const result = StreamableHTTPOptionsSchema.parse(validOptions);
|
||||
expect(result.type).toBe('streamable-http');
|
||||
});
|
||||
|
|
@ -113,7 +137,7 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
};
|
||||
|
||||
const result = StreamableHTTPOptionsSchema.parse(options);
|
||||
|
||||
|
||||
expect(result.headers).toEqual(options.headers);
|
||||
});
|
||||
});
|
||||
|
|
@ -165,7 +189,7 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
});
|
||||
|
||||
it('should process user ID in headers field', () => {
|
||||
const userId = 'test-user-123';
|
||||
const user = createTestUser({ id: 'test-user-123' });
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
|
|
@ -176,7 +200,7 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, userId);
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
Authorization: 'test-api-key-value',
|
||||
|
|
@ -217,15 +241,15 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
};
|
||||
|
||||
// Process for two different users
|
||||
const user1Id = 'user-123';
|
||||
const user2Id = 'user-456';
|
||||
const user1 = createTestUser({ id: 'user-123' });
|
||||
const user2 = createTestUser({ id: 'user-456' });
|
||||
|
||||
const resultUser1 = processMCPEnv(baseConfig, user1Id);
|
||||
const resultUser2 = processMCPEnv(baseConfig, user2Id);
|
||||
const resultUser1 = processMCPEnv(baseConfig, user1);
|
||||
const resultUser2 = processMCPEnv(baseConfig, user2);
|
||||
|
||||
// Verify each has the correct user ID
|
||||
expect('headers' in resultUser1 && resultUser1.headers?.['User-Id']).toBe(user1Id);
|
||||
expect('headers' in resultUser2 && resultUser2.headers?.['User-Id']).toBe(user2Id);
|
||||
expect('headers' in resultUser1 && resultUser1.headers?.['User-Id']).toBe('user-123');
|
||||
expect('headers' in resultUser2 && resultUser2.headers?.['User-Id']).toBe('user-456');
|
||||
|
||||
// Verify they're different objects
|
||||
expect(resultUser1).not.toBe(resultUser2);
|
||||
|
|
@ -239,11 +263,11 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
expect(baseConfig.headers?.['User-Id']).toBe('{{LIBRECHAT_USER_ID}}');
|
||||
|
||||
// Second user's config should be unchanged
|
||||
expect('headers' in resultUser2 && resultUser2.headers?.['User-Id']).toBe(user2Id);
|
||||
expect('headers' in resultUser2 && resultUser2.headers?.['User-Id']).toBe('user-456');
|
||||
});
|
||||
|
||||
it('should process headers in streamable-http options', () => {
|
||||
const userId = 'test-user-123';
|
||||
const user = createTestUser({ id: 'test-user-123' });
|
||||
const obj: MCPOptions = {
|
||||
type: 'streamable-http',
|
||||
url: 'https://example.com',
|
||||
|
|
@ -254,7 +278,7 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, userId);
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
Authorization: 'test-api-key-value',
|
||||
|
|
@ -262,7 +286,7 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
'Content-Type': 'application/json',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should maintain streamable-http type in processed options', () => {
|
||||
const obj: MCPOptions = {
|
||||
type: 'streamable-http',
|
||||
|
|
@ -273,5 +297,416 @@ describe('Environment Variable Extraction (MCP)', () => {
|
|||
|
||||
expect(result.type).toBe('streamable-http');
|
||||
});
|
||||
|
||||
it('should process dynamic user fields in headers', () => {
|
||||
const user = createTestUser({
|
||||
id: 'user-123',
|
||||
email: 'test@example.com',
|
||||
username: 'testuser',
|
||||
openidId: 'openid-123',
|
||||
googleId: 'google-456',
|
||||
emailVerified: true,
|
||||
role: 'admin',
|
||||
});
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
headers: {
|
||||
'User-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
'User-Name': '{{LIBRECHAT_USER_USERNAME}}',
|
||||
OpenID: '{{LIBRECHAT_USER_OPENIDID}}',
|
||||
'Google-ID': '{{LIBRECHAT_USER_GOOGLEID}}',
|
||||
'Email-Verified': '{{LIBRECHAT_USER_EMAILVERIFIED}}',
|
||||
'User-Role': '{{LIBRECHAT_USER_ROLE}}',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
'User-Email': 'test@example.com',
|
||||
'User-Name': 'testuser',
|
||||
OpenID: 'openid-123',
|
||||
'Google-ID': 'google-456',
|
||||
'Email-Verified': 'true',
|
||||
'User-Role': 'admin',
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle missing user fields gracefully', () => {
|
||||
const user = createTestUser({
|
||||
id: 'user-123',
|
||||
email: 'test@example.com',
|
||||
username: undefined, // explicitly set to undefined to test missing field
|
||||
});
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
headers: {
|
||||
'User-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
'User-Name': '{{LIBRECHAT_USER_USERNAME}}',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
'User-Email': 'test@example.com',
|
||||
'User-Name': '', // Empty string for missing field
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
});
|
||||
|
||||
it('should process user fields in env variables', () => {
|
||||
const user = createTestUser({
|
||||
id: 'user-123',
|
||||
email: 'test@example.com',
|
||||
ldapId: 'ldap-user-123',
|
||||
});
|
||||
const obj: MCPOptions = {
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
env: {
|
||||
USER_EMAIL: '{{LIBRECHAT_USER_EMAIL}}',
|
||||
LDAP_ID: '{{LIBRECHAT_USER_LDAPID}}',
|
||||
API_KEY: '${TEST_API_KEY}',
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('env' in result && result.env).toEqual({
|
||||
USER_EMAIL: 'test@example.com',
|
||||
LDAP_ID: 'ldap-user-123',
|
||||
API_KEY: 'test-api-key-value',
|
||||
});
|
||||
});
|
||||
|
||||
it('should process user fields in URL', () => {
|
||||
const user = createTestUser({
|
||||
id: 'user-123',
|
||||
username: 'testuser',
|
||||
});
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com/api/{{LIBRECHAT_USER_USERNAME}}/stream',
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('url' in result && result.url).toBe('https://example.com/api/testuser/stream');
|
||||
});
|
||||
|
||||
it('should handle boolean user fields', () => {
|
||||
const user = createTestUser({
|
||||
id: 'user-123',
|
||||
emailVerified: true,
|
||||
twoFactorEnabled: false,
|
||||
termsAccepted: true,
|
||||
});
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
headers: {
|
||||
'Email-Verified': '{{LIBRECHAT_USER_EMAILVERIFIED}}',
|
||||
'Two-Factor': '{{LIBRECHAT_USER_TWOFACTORENABLED}}',
|
||||
'Terms-Accepted': '{{LIBRECHAT_USER_TERMSACCEPTED}}',
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
'Email-Verified': 'true',
|
||||
'Two-Factor': 'false',
|
||||
'Terms-Accepted': 'true',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not process sensitive fields like password', () => {
|
||||
const user = createTestUser({
|
||||
id: 'user-123',
|
||||
email: 'test@example.com',
|
||||
password: 'secret-password',
|
||||
});
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
headers: {
|
||||
'User-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
'User-Password': '{{LIBRECHAT_USER_PASSWORD}}', // This should not be processed
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
'User-Email': 'test@example.com',
|
||||
'User-Password': '{{LIBRECHAT_USER_PASSWORD}}', // Unchanged
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle multiple occurrences of the same placeholder', () => {
|
||||
const user = createTestUser({
|
||||
id: 'user-123',
|
||||
email: 'test@example.com',
|
||||
});
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
headers: {
|
||||
'Primary-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
'Secondary-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
'Backup-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user);
|
||||
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
'Primary-Email': 'test@example.com',
|
||||
'Secondary-Email': 'test@example.com',
|
||||
'Backup-Email': 'test@example.com',
|
||||
});
|
||||
});
|
||||
|
||||
it('should support both id and _id properties for LIBRECHAT_USER_ID', () => {
|
||||
// Test with 'id' property
|
||||
const userWithId = createTestUser({
|
||||
id: 'user-123',
|
||||
email: 'test@example.com',
|
||||
});
|
||||
const obj1: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
headers: {
|
||||
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
||||
},
|
||||
};
|
||||
|
||||
const result1 = processMCPEnv(obj1, userWithId);
|
||||
expect('headers' in result1 && result1.headers?.['User-Id']).toBe('user-123');
|
||||
|
||||
// Test with '_id' property only (should not work since we only check 'id')
|
||||
const userWithUnderscore = createTestUser({
|
||||
id: undefined, // Remove default id to test _id
|
||||
_id: 'user-456',
|
||||
email: 'test@example.com',
|
||||
});
|
||||
const obj2: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
headers: {
|
||||
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
||||
},
|
||||
};
|
||||
|
||||
const result2 = processMCPEnv(obj2, userWithUnderscore);
|
||||
// Since we don't check _id, the placeholder should remain unchanged
|
||||
expect('headers' in result2 && result2.headers?.['User-Id']).toBe('{{LIBRECHAT_USER_ID}}');
|
||||
|
||||
// Test with both properties (id takes precedence)
|
||||
const userWithBoth = createTestUser({
|
||||
id: 'user-789',
|
||||
_id: 'user-000',
|
||||
email: 'test@example.com',
|
||||
});
|
||||
const obj3: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com',
|
||||
headers: {
|
||||
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
||||
},
|
||||
};
|
||||
|
||||
const result3 = processMCPEnv(obj3, userWithBoth);
|
||||
expect('headers' in result3 && result3.headers?.['User-Id']).toBe('user-789');
|
||||
});
|
||||
|
||||
it('should process customUserVars in env field', () => {
|
||||
const user = createTestUser();
|
||||
const customUserVars = {
|
||||
CUSTOM_VAR_1: 'custom-value-1',
|
||||
CUSTOM_VAR_2: 'custom-value-2',
|
||||
};
|
||||
const obj: MCPOptions = {
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
env: {
|
||||
VAR_A: '{{CUSTOM_VAR_1}}',
|
||||
VAR_B: 'Value with {{CUSTOM_VAR_2}}',
|
||||
VAR_C: '${TEST_API_KEY}',
|
||||
VAR_D: '{{LIBRECHAT_USER_EMAIL}}',
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user, customUserVars);
|
||||
|
||||
expect('env' in result && result.env).toEqual({
|
||||
VAR_A: 'custom-value-1',
|
||||
VAR_B: 'Value with custom-value-2',
|
||||
VAR_C: 'test-api-key-value',
|
||||
VAR_D: 'test@example.com',
|
||||
});
|
||||
});
|
||||
|
||||
it('should process customUserVars in headers field', () => {
|
||||
const user = createTestUser();
|
||||
const customUserVars = {
|
||||
USER_TOKEN: 'user-specific-token',
|
||||
REGION: 'us-west-1',
|
||||
};
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com/api',
|
||||
headers: {
|
||||
Authorization: 'Bearer {{USER_TOKEN}}',
|
||||
'X-Region': '{{REGION}}',
|
||||
'X-System-Key': '${TEST_API_KEY}',
|
||||
'X-User-Id': '{{LIBRECHAT_USER_ID}}',
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user, customUserVars);
|
||||
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
Authorization: 'Bearer user-specific-token',
|
||||
'X-Region': 'us-west-1',
|
||||
'X-System-Key': 'test-api-key-value',
|
||||
'X-User-Id': 'test-user-id',
|
||||
});
|
||||
});
|
||||
|
||||
it('should process customUserVars in URL field', () => {
|
||||
const user = createTestUser();
|
||||
const customUserVars = {
|
||||
API_VERSION: 'v2',
|
||||
TENANT_ID: 'tenant123',
|
||||
};
|
||||
const obj: MCPOptions = {
|
||||
type: 'websocket',
|
||||
url: 'wss://example.com/{{TENANT_ID}}/api/{{API_VERSION}}?user={{LIBRECHAT_USER_ID}}&key=${TEST_API_KEY}',
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user, customUserVars);
|
||||
|
||||
expect('url' in result && result.url).toBe(
|
||||
'wss://example.com/tenant123/api/v2?user=test-user-id&key=test-api-key-value',
|
||||
);
|
||||
});
|
||||
|
||||
it('should prioritize customUserVars over user fields and system env vars if placeholders are the same (though not recommended)', () => {
|
||||
// This tests the order of operations: customUserVars -> userFields -> systemEnv
|
||||
// BUt it's generally not recommended to have overlapping placeholder names.
|
||||
process.env.LIBRECHAT_USER_EMAIL = 'system-email-should-be-overridden';
|
||||
const user = createTestUser({ email: 'user-email-should-be-overridden' });
|
||||
const customUserVars = {
|
||||
LIBRECHAT_USER_EMAIL: 'custom-email-wins',
|
||||
};
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com/api',
|
||||
headers: {
|
||||
'Test-Email': '{{LIBRECHAT_USER_EMAIL}}', // Placeholder that could match custom, user, or system
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user, customUserVars);
|
||||
expect('headers' in result && result.headers?.['Test-Email']).toBe('custom-email-wins');
|
||||
|
||||
// Clean up env var
|
||||
delete process.env.LIBRECHAT_USER_EMAIL;
|
||||
});
|
||||
|
||||
it('should handle customUserVars with no matching placeholders', () => {
|
||||
const user = createTestUser();
|
||||
const customUserVars = {
|
||||
UNUSED_VAR: 'unused-value',
|
||||
};
|
||||
const obj: MCPOptions = {
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
env: {
|
||||
API_KEY: '${TEST_API_KEY}',
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user, customUserVars);
|
||||
expect('env' in result && result.env).toEqual({
|
||||
API_KEY: 'test-api-key-value',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle placeholders with no matching customUserVars (falling back to user/system vars)', () => {
|
||||
const user = createTestUser({ email: 'user-provided-email@example.com' });
|
||||
// No customUserVars provided or customUserVars is empty
|
||||
const customUserVars = {};
|
||||
const obj: MCPOptions = {
|
||||
type: 'sse',
|
||||
url: 'https://example.com/api',
|
||||
headers: {
|
||||
'User-Email-Header': '{{LIBRECHAT_USER_EMAIL}}', // Should use user.email
|
||||
'System-Key-Header': '${TEST_API_KEY}', // Should use process.env.TEST_API_KEY
|
||||
'Non-Existent-Custom': '{{NON_EXISTENT_CUSTOM_VAR}}', // Should remain as placeholder
|
||||
},
|
||||
};
|
||||
|
||||
const result = processMCPEnv(obj, user, customUserVars);
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
'User-Email-Header': 'user-provided-email@example.com',
|
||||
'System-Key-Header': 'test-api-key-value',
|
||||
'Non-Existent-Custom': '{{NON_EXISTENT_CUSTOM_VAR}}',
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly process a mix of all variable types', () => {
|
||||
const user = createTestUser({ id: 'userXYZ', username: 'john.doe' });
|
||||
const customUserVars = {
|
||||
CUSTOM_ENDPOINT_ID: 'ep123',
|
||||
ANOTHER_CUSTOM: 'another_val',
|
||||
};
|
||||
|
||||
const obj = {
|
||||
type: 'streamable-http' as const,
|
||||
url: 'https://{{CUSTOM_ENDPOINT_ID}}.example.com/users/{{LIBRECHAT_USER_USERNAME}}',
|
||||
headers: {
|
||||
'X-Auth-Token': '{{CUSTOM_TOKEN_FROM_USER_SETTINGS}}', // Assuming this would be a custom var
|
||||
'X-User-ID': '{{LIBRECHAT_USER_ID}}',
|
||||
'X-System-Test-Key': '${TEST_API_KEY}', // Using existing env var from beforeEach
|
||||
},
|
||||
env: {
|
||||
PROCESS_MODE: '{{PROCESS_MODE_CUSTOM}}', // Another custom var
|
||||
USER_HOME_DIR: '/home/{{LIBRECHAT_USER_USERNAME}}',
|
||||
SYSTEM_PATH: '${PATH}', // Example of a system env var
|
||||
},
|
||||
};
|
||||
|
||||
// Simulate customUserVars that would be passed, including those for headers and env
|
||||
const allCustomVarsForCall = {
|
||||
...customUserVars,
|
||||
CUSTOM_TOKEN_FROM_USER_SETTINGS: 'secretToken123!',
|
||||
PROCESS_MODE_CUSTOM: 'production',
|
||||
};
|
||||
|
||||
// Cast obj to MCPOptions when calling processMCPEnv.
|
||||
// This acknowledges the object might not strictly conform to one schema in the union,
|
||||
// but we are testing the function's ability to handle these properties if present.
|
||||
const result = processMCPEnv(obj as MCPOptions, user, allCustomVarsForCall);
|
||||
|
||||
expect('url' in result && result.url).toBe('https://ep123.example.com/users/john.doe');
|
||||
expect('headers' in result && result.headers).toEqual({
|
||||
'X-Auth-Token': 'secretToken123!',
|
||||
'X-User-ID': 'userXYZ',
|
||||
'X-System-Test-Key': 'test-api-key-value', // Expecting value of TEST_API_KEY
|
||||
});
|
||||
expect('env' in result && result.env).toEqual({
|
||||
PROCESS_MODE: 'production',
|
||||
USER_HOME_DIR: '/home/john.doe',
|
||||
SYSTEM_PATH: process.env.PATH, // Actual value of PATH from the test environment
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,15 +3,11 @@ import _axios from 'axios';
|
|||
import { URL } from 'url';
|
||||
import crypto from 'crypto';
|
||||
import { load } from 'js-yaml';
|
||||
import type {
|
||||
FunctionTool,
|
||||
Schema,
|
||||
Reference,
|
||||
ActionMetadata,
|
||||
ActionMetadataRuntime,
|
||||
} from './types/assistants';
|
||||
import type { ActionMetadata, ActionMetadataRuntime } from './types/agents';
|
||||
import type { FunctionTool, Schema, Reference } from './types/assistants';
|
||||
import { AuthTypeEnum, AuthorizationTypeEnum } from './types/agents';
|
||||
import type { OpenAPIV3 } from 'openapi-types';
|
||||
import { Tools, AuthTypeEnum, AuthorizationTypeEnum } from './types/assistants';
|
||||
import { Tools } from './types/assistants';
|
||||
|
||||
export type ParametersSchema = {
|
||||
type: string;
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ export const getUserPromptPreferences = () => `${prompts()}/preferences`;
|
|||
export const roles = () => '/api/roles';
|
||||
export const getRole = (roleName: string) => `${roles()}/${roleName.toLowerCase()}`;
|
||||
export const updatePromptPermissions = (roleName: string) => `${getRole(roleName)}/prompts`;
|
||||
export const updateMemoryPermissions = (roleName: string) => `${getRole(roleName)}/memories`;
|
||||
export const updateAgentPermissions = (roleName: string) => `${getRole(roleName)}/agents`;
|
||||
|
||||
/* Conversation Tags */
|
||||
|
|
@ -290,3 +291,8 @@ export const confirmTwoFactor = () => '/api/auth/2fa/confirm';
|
|||
export const disableTwoFactor = () => '/api/auth/2fa/disable';
|
||||
export const regenerateBackupCodes = () => '/api/auth/2fa/backup/regenerate';
|
||||
export const verifyTwoFactorTemp = () => '/api/auth/2fa/verify-temp';
|
||||
|
||||
/* Memories */
|
||||
export const memories = () => '/api/memories';
|
||||
export const memory = (key: string) => `${memories()}/${encodeURIComponent(key)}`;
|
||||
export const memoryPreferences = () => `${memories()}/preferences`;
|
||||
|
|
|
|||
|
|
@ -244,21 +244,26 @@ export const defaultAgentCapabilities = [
|
|||
AgentCapabilities.ocr,
|
||||
];
|
||||
|
||||
export const agentsEndpointSChema = baseEndpointSchema.merge(
|
||||
z.object({
|
||||
/* agents specific */
|
||||
recursionLimit: z.number().optional(),
|
||||
disableBuilder: z.boolean().optional(),
|
||||
maxRecursionLimit: z.number().optional(),
|
||||
allowedProviders: z.array(z.union([z.string(), eModelEndpointSchema])).optional(),
|
||||
capabilities: z
|
||||
.array(z.nativeEnum(AgentCapabilities))
|
||||
.optional()
|
||||
.default(defaultAgentCapabilities),
|
||||
}),
|
||||
);
|
||||
export const agentsEndpointSchema = baseEndpointSchema
|
||||
.merge(
|
||||
z.object({
|
||||
/* agents specific */
|
||||
recursionLimit: z.number().optional(),
|
||||
disableBuilder: z.boolean().optional().default(false),
|
||||
maxRecursionLimit: z.number().optional(),
|
||||
allowedProviders: z.array(z.union([z.string(), eModelEndpointSchema])).optional(),
|
||||
capabilities: z
|
||||
.array(z.nativeEnum(AgentCapabilities))
|
||||
.optional()
|
||||
.default(defaultAgentCapabilities),
|
||||
}),
|
||||
)
|
||||
.default({
|
||||
disableBuilder: false,
|
||||
capabilities: defaultAgentCapabilities,
|
||||
});
|
||||
|
||||
export type TAgentsEndpoint = z.infer<typeof agentsEndpointSChema>;
|
||||
export type TAgentsEndpoint = z.infer<typeof agentsEndpointSchema>;
|
||||
|
||||
export const endpointSchema = baseEndpointSchema.merge(
|
||||
z.object({
|
||||
|
|
@ -493,6 +498,7 @@ export const intefaceSchema = z
|
|||
sidePanel: z.boolean().optional(),
|
||||
multiConvo: z.boolean().optional(),
|
||||
bookmarks: z.boolean().optional(),
|
||||
memories: z.boolean().optional(),
|
||||
presets: z.boolean().optional(),
|
||||
prompts: z.boolean().optional(),
|
||||
agents: z.boolean().optional(),
|
||||
|
|
@ -508,6 +514,7 @@ export const intefaceSchema = z
|
|||
presets: true,
|
||||
multiConvo: true,
|
||||
bookmarks: true,
|
||||
memories: true,
|
||||
prompts: true,
|
||||
agents: true,
|
||||
temporaryChat: true,
|
||||
|
|
@ -581,11 +588,24 @@ export type TStartupConfig = {
|
|||
scraperType?: ScraperTypes;
|
||||
rerankerType?: RerankerTypes;
|
||||
};
|
||||
mcpServers?: Record<
|
||||
string,
|
||||
{
|
||||
customUserVars: Record<
|
||||
string,
|
||||
{
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
export enum OCRStrategy {
|
||||
MISTRAL_OCR = 'mistral_ocr',
|
||||
CUSTOM_OCR = 'custom_ocr',
|
||||
AZURE_MISTRAL_OCR = 'azure_mistral_ocr',
|
||||
}
|
||||
|
||||
export enum SearchCategories {
|
||||
|
|
@ -649,11 +669,35 @@ export const balanceSchema = z.object({
|
|||
refillAmount: z.number().optional().default(10000),
|
||||
});
|
||||
|
||||
export const memorySchema = z.object({
|
||||
disabled: z.boolean().optional(),
|
||||
validKeys: z.array(z.string()).optional(),
|
||||
tokenLimit: z.number().optional(),
|
||||
personalize: z.boolean().default(true),
|
||||
messageWindowSize: z.number().optional().default(5),
|
||||
agent: z
|
||||
.union([
|
||||
z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
provider: z.string(),
|
||||
model: z.string(),
|
||||
instructions: z.string().optional(),
|
||||
model_parameters: z.record(z.any()).optional(),
|
||||
}),
|
||||
])
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type TMemoryConfig = z.infer<typeof memorySchema>;
|
||||
|
||||
export const configSchema = z.object({
|
||||
version: z.string(),
|
||||
cache: z.boolean().default(true),
|
||||
ocr: ocrSchema.optional(),
|
||||
webSearch: webSearchSchema.optional(),
|
||||
memory: memorySchema.optional(),
|
||||
secureImageLinks: z.boolean().optional(),
|
||||
imageOutputType: z.nativeEnum(EImageOutputType).default(EImageOutputType.PNG),
|
||||
includedTools: z.array(z.string()).optional(),
|
||||
|
|
@ -694,7 +738,7 @@ export const configSchema = z.object({
|
|||
[EModelEndpoint.azureOpenAI]: azureEndpointSchema.optional(),
|
||||
[EModelEndpoint.azureAssistants]: assistantEndpointSchema.optional(),
|
||||
[EModelEndpoint.assistants]: assistantEndpointSchema.optional(),
|
||||
[EModelEndpoint.agents]: agentsEndpointSChema.optional(),
|
||||
[EModelEndpoint.agents]: agentsEndpointSchema.optional(),
|
||||
[EModelEndpoint.custom]: z.array(endpointSchema.partial()).optional(),
|
||||
[EModelEndpoint.bedrock]: baseEndpointSchema.optional(),
|
||||
})
|
||||
|
|
@ -853,7 +897,6 @@ export const defaultModels = {
|
|||
[EModelEndpoint.assistants]: [...sharedOpenAIModels, 'chatgpt-4o-latest'],
|
||||
[EModelEndpoint.agents]: sharedOpenAIModels, // TODO: Add agent models (agentsModels)
|
||||
[EModelEndpoint.google]: [
|
||||
// Shared Google Models between Vertex AI & Gen AI
|
||||
// Gemini 2.0 Models
|
||||
'gemini-2.0-flash-001',
|
||||
'gemini-2.0-flash-exp',
|
||||
|
|
@ -1104,6 +1147,10 @@ export enum CacheKeys {
|
|||
* Key for in-progress flow states.
|
||||
*/
|
||||
FLOWS = 'flows',
|
||||
/**
|
||||
* Key for individual MCP Tool Manifests.
|
||||
*/
|
||||
MCP_TOOLS = 'mcp_tools',
|
||||
/**
|
||||
* Key for pending chat requests (concurrency check)
|
||||
*/
|
||||
|
|
@ -1291,6 +1338,10 @@ export enum SettingsTabValues {
|
|||
* Chat input commands
|
||||
*/
|
||||
COMMANDS = 'commands',
|
||||
/**
|
||||
* Tab for Personalization Settings
|
||||
*/
|
||||
PERSONALIZATION = 'personalization',
|
||||
}
|
||||
|
||||
export enum STTProviders {
|
||||
|
|
@ -1328,7 +1379,7 @@ export enum Constants {
|
|||
/** Key for the app's version. */
|
||||
VERSION = 'v0.7.8',
|
||||
/** Key for the Custom Config's version (librechat.yaml). */
|
||||
CONFIG_VERSION = '1.2.6',
|
||||
CONFIG_VERSION = '1.2.8',
|
||||
/** Standard value for the first message's `parentMessageId` value, to indicate no parent exists. */
|
||||
NO_PARENT = '00000000-0000-0000-0000-000000000000',
|
||||
/** Standard value for the initial conversationId before a request is sent */
|
||||
|
|
@ -1355,6 +1406,8 @@ export enum Constants {
|
|||
GLOBAL_PROJECT_NAME = 'instance',
|
||||
/** Delimiter for MCP tools */
|
||||
mcp_delimiter = '_mcp_',
|
||||
/** Prefix for MCP plugins */
|
||||
mcp_prefix = 'mcp_',
|
||||
/** Placeholder Agent ID for Ephemeral Agents */
|
||||
EPHEMERAL_AGENT_ID = 'ephemeral',
|
||||
}
|
||||
|
|
@ -1398,6 +1451,10 @@ export enum LocalStorageKeys {
|
|||
LAST_CODE_TOGGLE_ = 'LAST_CODE_TOGGLE_',
|
||||
/** Last checked toggle for Web Search per conversation ID */
|
||||
LAST_WEB_SEARCH_TOGGLE_ = 'LAST_WEB_SEARCH_TOGGLE_',
|
||||
/** Key for the last selected agent provider */
|
||||
LAST_AGENT_PROVIDER = 'lastAgentProvider',
|
||||
/** Key for the last selected agent model */
|
||||
LAST_AGENT_MODEL = 'lastAgentModel',
|
||||
}
|
||||
|
||||
export enum ForkOptions {
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ export default function createPayload(submission: t.TSubmission) {
|
|||
ephemeralAgent,
|
||||
} = submission;
|
||||
const { conversationId } = s.tConvoUpdateSchema.parse(conversation);
|
||||
const { endpoint, endpointType } = endpointOption as {
|
||||
const { endpoint: _e, endpointType } = endpointOption as {
|
||||
endpoint: s.EModelEndpoint;
|
||||
endpointType?: s.EModelEndpoint;
|
||||
};
|
||||
|
||||
const endpoint = _e as s.EModelEndpoint;
|
||||
let server = EndpointURLs[endpointType ?? endpoint];
|
||||
const isEphemeral = s.isEphemeralAgent(endpoint, ephemeralAgent);
|
||||
|
||||
|
|
@ -32,6 +32,7 @@ export default function createPayload(submission: t.TSubmission) {
|
|||
const payload: t.TPayload = {
|
||||
...userMessage,
|
||||
...endpointOption,
|
||||
endpoint,
|
||||
ephemeralAgent: isEphemeral ? ephemeralAgent : undefined,
|
||||
isContinued: !!(isEdited && isContinued),
|
||||
conversationId,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { AxiosResponse } from 'axios';
|
|||
import type * as t from './types';
|
||||
import * as endpoints from './api-endpoints';
|
||||
import * as a from './types/assistants';
|
||||
import * as ag from './types/agents';
|
||||
import * as m from './types/mutations';
|
||||
import * as q from './types/queries';
|
||||
import * as f from './types/files';
|
||||
|
|
@ -150,7 +151,11 @@ export const updateUserPlugins = (payload: t.TUpdateUserPlugins) => {
|
|||
|
||||
/* Config */
|
||||
|
||||
export const getStartupConfig = (): Promise<config.TStartupConfig> => {
|
||||
export const getStartupConfig = (): Promise<
|
||||
config.TStartupConfig & {
|
||||
mcpCustomUserVars?: Record<string, { title: string; description: string }>;
|
||||
}
|
||||
> => {
|
||||
return request.get(endpoints.config());
|
||||
};
|
||||
|
||||
|
|
@ -351,7 +356,7 @@ export const updateAction = (data: m.UpdateActionVariables): Promise<m.UpdateAct
|
|||
);
|
||||
};
|
||||
|
||||
export function getActions(): Promise<a.Action[]> {
|
||||
export function getActions(): Promise<ag.Action[]> {
|
||||
return request.get(
|
||||
endpoints.agents({
|
||||
path: 'actions',
|
||||
|
|
@ -407,7 +412,7 @@ export const updateAgent = ({
|
|||
|
||||
export const duplicateAgent = ({
|
||||
agent_id,
|
||||
}: m.DuplicateAgentBody): Promise<{ agent: a.Agent; actions: a.Action[] }> => {
|
||||
}: m.DuplicateAgentBody): Promise<{ agent: a.Agent; actions: ag.Action[] }> => {
|
||||
return request.post(
|
||||
endpoints.agents({
|
||||
path: `${agent_id}/duplicate`,
|
||||
|
|
@ -733,6 +738,12 @@ export function updateAgentPermissions(
|
|||
return request.put(endpoints.updateAgentPermissions(variables.roleName), variables.updates);
|
||||
}
|
||||
|
||||
export function updateMemoryPermissions(
|
||||
variables: m.UpdateMemoryPermVars,
|
||||
): Promise<m.UpdatePermResponse> {
|
||||
return request.put(endpoints.updateMemoryPermissions(variables.roleName), variables.updates);
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
export function getConversationTags(): Promise<t.TConversationTagsResponse> {
|
||||
return request.get(endpoints.conversationTags());
|
||||
|
|
@ -814,3 +825,33 @@ export function verifyTwoFactorTemp(
|
|||
): Promise<t.TVerify2FATempResponse> {
|
||||
return request.post(endpoints.verifyTwoFactorTemp(), payload);
|
||||
}
|
||||
|
||||
/* Memories */
|
||||
export const getMemories = (): Promise<q.MemoriesResponse> => {
|
||||
return request.get(endpoints.memories());
|
||||
};
|
||||
|
||||
export const deleteMemory = (key: string): Promise<void> => {
|
||||
return request.delete(endpoints.memory(key));
|
||||
};
|
||||
|
||||
export const updateMemory = (
|
||||
key: string,
|
||||
value: string,
|
||||
originalKey?: string,
|
||||
): Promise<q.TUserMemory> => {
|
||||
return request.patch(endpoints.memory(originalKey || key), { key, value });
|
||||
};
|
||||
|
||||
export const updateMemoryPreferences = (preferences: {
|
||||
memories: boolean;
|
||||
}): Promise<{ updated: boolean; preferences: { memories: boolean } }> => {
|
||||
return request.patch(endpoints.memoryPreferences(), preferences);
|
||||
};
|
||||
|
||||
export const createMemory = (data: {
|
||||
key: string;
|
||||
value: string;
|
||||
}): Promise<{ created: boolean; memory: q.TUserMemory }> => {
|
||||
return request.post(endpoints.memories(), data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { z } from 'zod';
|
||||
import { EModelEndpoint } from './schemas';
|
||||
import type { FileConfig, EndpointFileConfig } from './types/files';
|
||||
import type { EndpointFileConfig, FileConfig } from './types/files';
|
||||
|
||||
export const supportsFiles = {
|
||||
[EModelEndpoint.openAI]: true,
|
||||
|
|
@ -49,6 +49,8 @@ export const fullMimeTypesList = [
|
|||
'text/javascript',
|
||||
'image/gif',
|
||||
'image/png',
|
||||
'image/heic',
|
||||
'image/heif',
|
||||
'application/x-tar',
|
||||
'application/typescript',
|
||||
'application/xml',
|
||||
|
|
@ -80,6 +82,8 @@ export const codeInterpreterMimeTypesList = [
|
|||
'text/javascript',
|
||||
'image/gif',
|
||||
'image/png',
|
||||
'image/heic',
|
||||
'image/heif',
|
||||
'application/x-tar',
|
||||
'application/typescript',
|
||||
'application/xml',
|
||||
|
|
@ -105,18 +109,18 @@ export const retrievalMimeTypesList = [
|
|||
'text/plain',
|
||||
];
|
||||
|
||||
export const imageExtRegex = /\.(jpg|jpeg|png|gif|webp)$/i;
|
||||
export const imageExtRegex = /\.(jpg|jpeg|png|gif|webp|heic|heif)$/i;
|
||||
|
||||
export const excelMimeTypes =
|
||||
/^application\/(vnd\.ms-excel|msexcel|x-msexcel|x-ms-excel|x-excel|x-dos_ms_excel|xls|x-xls|vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet)$/;
|
||||
|
||||
export const textMimeTypes =
|
||||
/^(text\/(x-c|x-csharp|tab-separated-values|x-c\+\+|x-java|html|markdown|x-php|x-python|x-script\.python|x-ruby|x-tex|plain|css|vtt|javascript|csv))$/;
|
||||
/^(text\/(x-c|x-csharp|tab-separated-values|x-c\+\+|x-h|x-java|html|markdown|x-php|x-python|x-script\.python|x-ruby|x-tex|plain|css|vtt|javascript|csv))$/;
|
||||
|
||||
export const applicationMimeTypes =
|
||||
/^(application\/(epub\+zip|csv|json|pdf|x-tar|typescript|vnd\.openxmlformats-officedocument\.(wordprocessingml\.document|presentationml\.presentation|spreadsheetml\.sheet)|xml|zip))$/;
|
||||
|
||||
export const imageMimeTypes = /^image\/(jpeg|gif|png|webp)$/;
|
||||
export const imageMimeTypes = /^image\/(jpeg|gif|png|webp|heic|heif)$/;
|
||||
|
||||
export const supportedMimeTypes = [
|
||||
textMimeTypes,
|
||||
|
|
@ -138,6 +142,7 @@ export const codeTypeMapping: { [key: string]: string } = {
|
|||
c: 'text/x-c',
|
||||
cs: 'text/x-csharp',
|
||||
cpp: 'text/x-c++',
|
||||
h: 'text/x-h',
|
||||
md: 'text/markdown',
|
||||
php: 'text/x-php',
|
||||
py: 'text/x-python',
|
||||
|
|
@ -155,7 +160,7 @@ export const codeTypeMapping: { [key: string]: string } = {
|
|||
};
|
||||
|
||||
export const retrievalMimeTypes = [
|
||||
/^(text\/(x-c|x-c\+\+|html|x-java|markdown|x-php|x-python|x-script\.python|x-ruby|x-tex|plain|vtt|xml))$/,
|
||||
/^(text\/(x-c|x-c\+\+|x-h|html|x-java|markdown|x-php|x-python|x-script\.python|x-ruby|x-tex|plain|vtt|xml))$/,
|
||||
/^(application\/(json|pdf|vnd\.openxmlformats-officedocument\.(wordprocessingml\.document|presentationml\.presentation)))$/,
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ export * from './models';
|
|||
export * from './mcp';
|
||||
/* web search */
|
||||
export * from './web';
|
||||
/* memory */
|
||||
export * from './memory';
|
||||
/* RBAC */
|
||||
export * from './permissions';
|
||||
export * from './roles';
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ export enum QueryKeys {
|
|||
health = 'health',
|
||||
userTerms = 'userTerms',
|
||||
banner = 'banner',
|
||||
/* Memories */
|
||||
memories = 'memories',
|
||||
}
|
||||
|
||||
export enum MutationKeys {
|
||||
|
|
@ -71,4 +73,5 @@ export enum MutationKeys {
|
|||
updateRole = 'updateRole',
|
||||
enableTwoFactor = 'enableTwoFactor',
|
||||
verifyTwoFactor = 'verifyTwoFactor',
|
||||
updateMemoryPreferences = 'updateMemoryPreferences',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { z } from 'zod';
|
||||
import type { TUser } from './types';
|
||||
import { extractEnvVariable } from './utils';
|
||||
import { TokenExchangeMethodEnum } from './types/agents';
|
||||
|
||||
const BaseOptionsSchema = z.object({
|
||||
iconPath: z.string().optional(),
|
||||
|
|
@ -7,6 +9,45 @@ const BaseOptionsSchema = z.object({
|
|||
initTimeout: z.number().optional(),
|
||||
/** Controls visibility in chat dropdown menu (MCPSelect) */
|
||||
chatMenu: z.boolean().optional(),
|
||||
/**
|
||||
* Controls server instruction behavior:
|
||||
* - undefined/not set: No instructions included (default)
|
||||
* - true: Use server-provided instructions
|
||||
* - string: Use custom instructions (overrides server-provided)
|
||||
*/
|
||||
serverInstructions: z.union([z.boolean(), z.string()]).optional(),
|
||||
/**
|
||||
* OAuth configuration for SSE and Streamable HTTP transports
|
||||
* - Optional: OAuth can be auto-discovered on 401 responses
|
||||
* - Pre-configured values will skip discovery steps
|
||||
*/
|
||||
oauth: z
|
||||
.object({
|
||||
/** OAuth authorization endpoint (optional - can be auto-discovered) */
|
||||
authorization_url: z.string().url().optional(),
|
||||
/** OAuth token endpoint (optional - can be auto-discovered) */
|
||||
token_url: z.string().url().optional(),
|
||||
/** OAuth client ID (optional - can use dynamic registration) */
|
||||
client_id: z.string().optional(),
|
||||
/** OAuth client secret (optional - can use dynamic registration) */
|
||||
client_secret: z.string().optional(),
|
||||
/** OAuth scopes to request */
|
||||
scope: z.string().optional(),
|
||||
/** OAuth redirect URI (defaults to /api/mcp/{serverName}/oauth/callback) */
|
||||
redirect_uri: z.string().url().optional(),
|
||||
/** Token exchange method */
|
||||
token_exchange_method: z.nativeEnum(TokenExchangeMethodEnum).optional(),
|
||||
})
|
||||
.optional(),
|
||||
customUserVars: z
|
||||
.record(
|
||||
z.string(),
|
||||
z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const StdioOptionsSchema = BaseOptionsSchema.extend({
|
||||
|
|
@ -114,12 +155,100 @@ export const MCPServersSchema = z.record(z.string(), MCPOptionsSchema);
|
|||
export type MCPOptions = z.infer<typeof MCPOptionsSchema>;
|
||||
|
||||
/**
|
||||
* Recursively processes an object to replace environment variables in string values
|
||||
* @param {MCPOptions} obj - The object to process
|
||||
* @param {string} [userId] - The user ID
|
||||
* @returns {MCPOptions} - The processed object with environment variables replaced
|
||||
* List of allowed user fields that can be used in MCP environment variables.
|
||||
* These are non-sensitive string/boolean fields from the IUser interface.
|
||||
*/
|
||||
export function processMCPEnv(obj: Readonly<MCPOptions>, userId?: string): MCPOptions {
|
||||
const ALLOWED_USER_FIELDS = [
|
||||
'name',
|
||||
'username',
|
||||
'email',
|
||||
'provider',
|
||||
'role',
|
||||
'googleId',
|
||||
'facebookId',
|
||||
'openidId',
|
||||
'samlId',
|
||||
'ldapId',
|
||||
'githubId',
|
||||
'discordId',
|
||||
'appleId',
|
||||
'emailVerified',
|
||||
'twoFactorEnabled',
|
||||
'termsAccepted',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Processes a string value to replace user field placeholders
|
||||
* @param value - The string value to process
|
||||
* @param user - The user object
|
||||
* @returns The processed string with placeholders replaced
|
||||
*/
|
||||
function processUserPlaceholders(value: string, user?: TUser): string {
|
||||
if (!user || typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
|
||||
for (const field of ALLOWED_USER_FIELDS) {
|
||||
const placeholder = `{{LIBRECHAT_USER_${field.toUpperCase()}}}`;
|
||||
if (value.includes(placeholder)) {
|
||||
const fieldValue = user[field as keyof TUser];
|
||||
const replacementValue = fieldValue != null ? String(fieldValue) : '';
|
||||
value = value.replace(new RegExp(placeholder, 'g'), replacementValue);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function processSingleValue({
|
||||
originalValue,
|
||||
customUserVars,
|
||||
user,
|
||||
}: {
|
||||
originalValue: string;
|
||||
customUserVars?: Record<string, string>;
|
||||
user?: TUser;
|
||||
}): string {
|
||||
let value = originalValue;
|
||||
|
||||
// 1. Replace custom user variables
|
||||
if (customUserVars) {
|
||||
for (const [varName, varVal] of Object.entries(customUserVars)) {
|
||||
/** Escaped varName for use in regex to avoid issues with special characters */
|
||||
const escapedVarName = varName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const placeholderRegex = new RegExp(`\\{\\{${escapedVarName}\\}\\}`, 'g');
|
||||
value = value.replace(placeholderRegex, varVal);
|
||||
}
|
||||
}
|
||||
|
||||
// 2.A. Special handling for LIBRECHAT_USER_ID placeholder
|
||||
// This ensures {{LIBRECHAT_USER_ID}} is replaced only if user.id is available.
|
||||
// If user.id is null/undefined, the placeholder remains
|
||||
if (user && user.id != null && value.includes('{{LIBRECHAT_USER_ID}}')) {
|
||||
value = value.replace(/\{\{LIBRECHAT_USER_ID\}\}/g, String(user.id));
|
||||
}
|
||||
|
||||
// 2.B. Replace other standard user field placeholders (e.g., {{LIBRECHAT_USER_EMAIL}})
|
||||
value = processUserPlaceholders(value, user);
|
||||
|
||||
// 3. Replace system environment variables
|
||||
value = extractEnvVariable(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively processes an object to replace environment variables in string values
|
||||
* @param obj - The object to process
|
||||
* @param user - The user object containing all user fields
|
||||
* @param customUserVars - vars that user set in settings
|
||||
* @returns - The processed object with environment variables replaced
|
||||
*/
|
||||
export function processMCPEnv(
|
||||
obj: Readonly<MCPOptions>,
|
||||
user?: TUser,
|
||||
customUserVars?: Record<string, string>,
|
||||
): MCPOptions {
|
||||
if (obj === null || obj === undefined) {
|
||||
return obj;
|
||||
}
|
||||
|
|
@ -128,24 +257,25 @@ export function processMCPEnv(obj: Readonly<MCPOptions>, userId?: string): MCPOp
|
|||
|
||||
if ('env' in newObj && newObj.env) {
|
||||
const processedEnv: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(newObj.env)) {
|
||||
processedEnv[key] = extractEnvVariable(value);
|
||||
for (const [key, originalValue] of Object.entries(newObj.env)) {
|
||||
processedEnv[key] = processSingleValue({ originalValue, customUserVars, user });
|
||||
}
|
||||
newObj.env = processedEnv;
|
||||
} else if ('headers' in newObj && newObj.headers) {
|
||||
}
|
||||
|
||||
// Process headers if they exist (for WebSocket, SSE, StreamableHTTP types)
|
||||
// Note: `env` and `headers` are on different branches of the MCPOptions union type.
|
||||
if ('headers' in newObj && newObj.headers) {
|
||||
const processedHeaders: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(newObj.headers)) {
|
||||
if (value === '{{LIBRECHAT_USER_ID}}' && userId != null && userId) {
|
||||
processedHeaders[key] = userId;
|
||||
continue;
|
||||
}
|
||||
processedHeaders[key] = extractEnvVariable(value);
|
||||
for (const [key, originalValue] of Object.entries(newObj.headers)) {
|
||||
processedHeaders[key] = processSingleValue({ originalValue, customUserVars, user });
|
||||
}
|
||||
newObj.headers = processedHeaders;
|
||||
}
|
||||
|
||||
// Process URL if it exists (for WebSocket, SSE, StreamableHTTP types)
|
||||
if ('url' in newObj && newObj.url) {
|
||||
newObj.url = extractEnvVariable(newObj.url);
|
||||
newObj.url = processSingleValue({ originalValue: newObj.url, customUserVars, user });
|
||||
}
|
||||
|
||||
return newObj;
|
||||
|
|
|
|||
62
packages/data-provider/src/memory.ts
Normal file
62
packages/data-provider/src/memory.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import type { TCustomConfig, TMemoryConfig } from './config';
|
||||
|
||||
/**
|
||||
* Loads the memory configuration and validates it
|
||||
* @param config - The memory configuration from librechat.yaml
|
||||
* @returns The validated memory configuration
|
||||
*/
|
||||
export function loadMemoryConfig(config: TCustomConfig['memory']): TMemoryConfig | undefined {
|
||||
if (!config) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If disabled is explicitly true, return the config as-is
|
||||
if (config.disabled === true) {
|
||||
return config;
|
||||
}
|
||||
|
||||
// Check if the agent configuration is valid
|
||||
const hasValidAgent =
|
||||
config.agent &&
|
||||
(('id' in config.agent && !!config.agent.id) ||
|
||||
('provider' in config.agent &&
|
||||
'model' in config.agent &&
|
||||
!!config.agent.provider &&
|
||||
!!config.agent.model));
|
||||
|
||||
// If agent config is invalid, treat as disabled
|
||||
if (!hasValidAgent) {
|
||||
return {
|
||||
...config,
|
||||
disabled: true,
|
||||
};
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if memory feature is enabled based on the configuration
|
||||
* @param config - The memory configuration
|
||||
* @returns True if memory is enabled, false otherwise
|
||||
*/
|
||||
export function isMemoryEnabled(config: TMemoryConfig | undefined): boolean {
|
||||
if (!config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.disabled === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if agent configuration is valid
|
||||
const hasValidAgent =
|
||||
config.agent &&
|
||||
(('id' in config.agent && !!config.agent.id) ||
|
||||
('provider' in config.agent &&
|
||||
'model' in config.agent &&
|
||||
!!config.agent.provider &&
|
||||
!!config.agent.model));
|
||||
|
||||
return !!hasValidAgent;
|
||||
}
|
||||
|
|
@ -225,13 +225,15 @@ const extractOmniVersion = (modelStr: string): string => {
|
|||
export const getResponseSender = (endpointOption: t.TEndpointOption): string => {
|
||||
const {
|
||||
model: _m,
|
||||
endpoint,
|
||||
endpoint: _e,
|
||||
endpointType,
|
||||
modelDisplayLabel: _mdl,
|
||||
chatGptLabel: _cgl,
|
||||
modelLabel: _ml,
|
||||
} = endpointOption;
|
||||
|
||||
const endpoint = _e as EModelEndpoint;
|
||||
|
||||
const model = _m ?? '';
|
||||
const modelDisplayLabel = _mdl ?? '';
|
||||
const chatGptLabel = _cgl ?? '';
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ export enum PermissionTypes {
|
|||
* Type for Agent Permissions
|
||||
*/
|
||||
AGENTS = 'AGENTS',
|
||||
/**
|
||||
* Type for Memory Permissions
|
||||
*/
|
||||
MEMORIES = 'MEMORIES',
|
||||
/**
|
||||
* Type for Multi-Conversation Permissions
|
||||
*/
|
||||
|
|
@ -45,6 +49,8 @@ export enum Permissions {
|
|||
READ = 'READ',
|
||||
READ_AUTHOR = 'READ_AUTHOR',
|
||||
SHARE = 'SHARE',
|
||||
/** Can disable if desired */
|
||||
OPT_OUT = 'OPT_OUT',
|
||||
}
|
||||
|
||||
export const promptPermissionsSchema = z.object({
|
||||
|
|
@ -60,6 +66,15 @@ export const bookmarkPermissionsSchema = z.object({
|
|||
});
|
||||
export type TBookmarkPermissions = z.infer<typeof bookmarkPermissionsSchema>;
|
||||
|
||||
export const memoryPermissionsSchema = z.object({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
[Permissions.CREATE]: z.boolean().default(true),
|
||||
[Permissions.UPDATE]: z.boolean().default(true),
|
||||
[Permissions.READ]: z.boolean().default(true),
|
||||
[Permissions.OPT_OUT]: z.boolean().default(true),
|
||||
});
|
||||
export type TMemoryPermissions = z.infer<typeof memoryPermissionsSchema>;
|
||||
|
||||
export const agentPermissionsSchema = z.object({
|
||||
[Permissions.SHARED_GLOBAL]: z.boolean().default(false),
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
|
|
@ -92,6 +107,7 @@ export type TWebSearchPermissions = z.infer<typeof webSearchPermissionsSchema>;
|
|||
export const permissionsSchema = z.object({
|
||||
[PermissionTypes.PROMPTS]: promptPermissionsSchema,
|
||||
[PermissionTypes.BOOKMARKS]: bookmarkPermissionsSchema,
|
||||
[PermissionTypes.MEMORIES]: memoryPermissionsSchema,
|
||||
[PermissionTypes.AGENTS]: agentPermissionsSchema,
|
||||
[PermissionTypes.MULTI_CONVO]: multiConvoPermissionsSchema,
|
||||
[PermissionTypes.TEMPORARY_CHAT]: temporaryChatPermissionsSchema,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
permissionsSchema,
|
||||
agentPermissionsSchema,
|
||||
promptPermissionsSchema,
|
||||
memoryPermissionsSchema,
|
||||
runCodePermissionsSchema,
|
||||
webSearchPermissionsSchema,
|
||||
bookmarkPermissionsSchema,
|
||||
|
|
@ -48,6 +49,13 @@ const defaultRolesSchema = z.object({
|
|||
[PermissionTypes.BOOKMARKS]: bookmarkPermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.MEMORIES]: memoryPermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
[Permissions.CREATE]: z.boolean().default(true),
|
||||
[Permissions.UPDATE]: z.boolean().default(true),
|
||||
[Permissions.READ]: z.boolean().default(true),
|
||||
[Permissions.OPT_OUT]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.AGENTS]: agentPermissionsSchema.extend({
|
||||
[Permissions.SHARED_GLOBAL]: z.boolean().default(true),
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
|
|
@ -86,6 +94,13 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[PermissionTypes.BOOKMARKS]: {
|
||||
[Permissions.USE]: true,
|
||||
},
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.UPDATE]: true,
|
||||
[Permissions.READ]: true,
|
||||
[Permissions.OPT_OUT]: true,
|
||||
},
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: true,
|
||||
[Permissions.USE]: true,
|
||||
|
|
@ -110,6 +125,7 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
permissions: {
|
||||
[PermissionTypes.PROMPTS]: {},
|
||||
[PermissionTypes.BOOKMARKS]: {},
|
||||
[PermissionTypes.MEMORIES]: {},
|
||||
[PermissionTypes.AGENTS]: {},
|
||||
[PermissionTypes.MULTI_CONVO]: {},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: {},
|
||||
|
|
|
|||
|
|
@ -522,11 +522,19 @@ export const tMessageSchema = z.object({
|
|||
feedback: feedbackSchema.optional(),
|
||||
});
|
||||
|
||||
export type MemoryArtifact = {
|
||||
key: string;
|
||||
value?: string;
|
||||
tokenCount?: number;
|
||||
type: 'update' | 'delete';
|
||||
};
|
||||
|
||||
export type TAttachmentMetadata = {
|
||||
type?: Tools;
|
||||
messageId: string;
|
||||
toolCallId: string;
|
||||
[Tools.web_search]?: SearchResultData;
|
||||
[Tools.memory]?: MemoryArtifact;
|
||||
};
|
||||
|
||||
export type TAttachment =
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import type OpenAI from 'openai';
|
||||
import type { InfiniteData } from '@tanstack/react-query';
|
||||
import type {
|
||||
TBanner,
|
||||
TMessage,
|
||||
TResPlugin,
|
||||
ImageDetail,
|
||||
TSharedLink,
|
||||
TConversation,
|
||||
EModelEndpoint,
|
||||
TConversationTag,
|
||||
TBanner,
|
||||
TAttachment,
|
||||
} from './schemas';
|
||||
import { TMinimalFeedback } from './feedback';
|
||||
import { SettingDefinition } from './generate';
|
||||
import type { SettingDefinition } from './generate';
|
||||
import type { TMinimalFeedback } from './feedback';
|
||||
import type { Agent } from './types/assistants';
|
||||
|
||||
export type TOpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam;
|
||||
|
||||
|
|
@ -20,28 +21,78 @@ export * from './schemas';
|
|||
export type TMessages = TMessage[];
|
||||
|
||||
/* TODO: Cleanup EndpointOption types */
|
||||
export type TEndpointOption = {
|
||||
spec?: string | null;
|
||||
iconURL?: string | null;
|
||||
endpoint: EModelEndpoint;
|
||||
endpointType?: EModelEndpoint;
|
||||
export type TEndpointOption = Pick<
|
||||
TConversation,
|
||||
// Core conversation fields
|
||||
| 'endpoint'
|
||||
| 'endpointType'
|
||||
| 'model'
|
||||
| 'modelLabel'
|
||||
| 'chatGptLabel'
|
||||
| 'promptPrefix'
|
||||
| 'temperature'
|
||||
| 'topP'
|
||||
| 'topK'
|
||||
| 'top_p'
|
||||
| 'frequency_penalty'
|
||||
| 'presence_penalty'
|
||||
| 'maxOutputTokens'
|
||||
| 'maxContextTokens'
|
||||
| 'max_tokens'
|
||||
| 'maxTokens'
|
||||
| 'resendFiles'
|
||||
| 'imageDetail'
|
||||
| 'reasoning_effort'
|
||||
| 'instructions'
|
||||
| 'additional_instructions'
|
||||
| 'append_current_datetime'
|
||||
| 'tools'
|
||||
| 'stop'
|
||||
| 'region'
|
||||
| 'additionalModelRequestFields'
|
||||
// Anthropic-specific
|
||||
| 'promptCache'
|
||||
| 'thinking'
|
||||
| 'thinkingBudget'
|
||||
// Assistant/Agent fields
|
||||
| 'assistant_id'
|
||||
| 'agent_id'
|
||||
// UI/Display fields
|
||||
| 'iconURL'
|
||||
| 'greeting'
|
||||
| 'spec'
|
||||
// Artifacts
|
||||
| 'artifacts'
|
||||
// Files
|
||||
| 'file_ids'
|
||||
// System field
|
||||
| 'system'
|
||||
// Google examples
|
||||
| 'examples'
|
||||
// Context
|
||||
| 'context'
|
||||
> & {
|
||||
// Fields specific to endpoint options that don't exist on TConversation
|
||||
modelDisplayLabel?: string;
|
||||
resendFiles?: boolean;
|
||||
promptCache?: boolean;
|
||||
maxContextTokens?: number;
|
||||
imageDetail?: ImageDetail;
|
||||
model?: string | null;
|
||||
promptPrefix?: string;
|
||||
temperature?: number;
|
||||
chatGptLabel?: string | null;
|
||||
modelLabel?: string | null;
|
||||
jailbreak?: boolean;
|
||||
key?: string | null;
|
||||
/* assistant */
|
||||
/** @deprecated Assistants API */
|
||||
thread_id?: string;
|
||||
/* multi-response stream */
|
||||
// Conversation identifiers for multi-response streams
|
||||
overrideConvoId?: string;
|
||||
overrideUserMessageId?: string;
|
||||
// Model parameters (used by different endpoints)
|
||||
modelOptions?: Record<string, unknown>;
|
||||
model_parameters?: Record<string, unknown>;
|
||||
// Configuration data (added by middleware)
|
||||
modelsConfig?: TModelsConfig;
|
||||
// File attachments (processed by middleware)
|
||||
attachments?: TAttachment[];
|
||||
// Generated prompts
|
||||
artifactsPrompt?: string;
|
||||
// Agent-specific fields
|
||||
agent?: Promise<Agent>;
|
||||
// Client-specific options
|
||||
clientOptions?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type TEphemeralAgent = {
|
||||
|
|
@ -130,6 +181,9 @@ export type TUser = {
|
|||
plugins?: string[];
|
||||
twoFactorEnabled?: boolean;
|
||||
backupCodes?: TBackupCode[];
|
||||
personalization?: {
|
||||
memories?: boolean;
|
||||
};
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
import { StepTypes, ContentTypes, ToolCallTypes } from './runs';
|
||||
import type { TAttachment, TPlugin } from 'src/schemas';
|
||||
import type { FunctionToolCall } from './assistants';
|
||||
import type { TAttachment } from 'src/schemas';
|
||||
|
||||
export namespace Agents {
|
||||
export type MessageType = 'human' | 'ai' | 'generic' | 'system' | 'function' | 'tool' | 'remove';
|
||||
|
|
@ -279,3 +279,79 @@ export type ToolCallResult = {
|
|||
conversationId: string;
|
||||
attachments?: TAttachment[];
|
||||
};
|
||||
|
||||
export enum AuthTypeEnum {
|
||||
ServiceHttp = 'service_http',
|
||||
OAuth = 'oauth',
|
||||
None = 'none',
|
||||
}
|
||||
|
||||
export enum AuthorizationTypeEnum {
|
||||
Bearer = 'bearer',
|
||||
Basic = 'basic',
|
||||
Custom = 'custom',
|
||||
}
|
||||
|
||||
export enum TokenExchangeMethodEnum {
|
||||
DefaultPost = 'default_post',
|
||||
BasicAuthHeader = 'basic_auth_header',
|
||||
}
|
||||
|
||||
export type Action = {
|
||||
action_id: string;
|
||||
type?: string;
|
||||
settings?: Record<string, unknown>;
|
||||
metadata: ActionMetadata;
|
||||
version: number | string;
|
||||
} & ({ assistant_id: string; agent_id?: never } | { assistant_id?: never; agent_id: string });
|
||||
|
||||
export type ActionMetadata = {
|
||||
api_key?: string;
|
||||
auth?: ActionAuth;
|
||||
domain?: string;
|
||||
privacy_policy_url?: string;
|
||||
raw_spec?: string;
|
||||
oauth_client_id?: string;
|
||||
oauth_client_secret?: string;
|
||||
};
|
||||
|
||||
export type ActionAuth = {
|
||||
authorization_type?: AuthorizationTypeEnum;
|
||||
custom_auth_header?: string;
|
||||
type?: AuthTypeEnum;
|
||||
authorization_content_type?: string;
|
||||
authorization_url?: string;
|
||||
client_url?: string;
|
||||
scope?: string;
|
||||
token_exchange_method?: TokenExchangeMethodEnum;
|
||||
};
|
||||
|
||||
export type ActionMetadataRuntime = ActionMetadata & {
|
||||
oauth_access_token?: string;
|
||||
oauth_refresh_token?: string;
|
||||
oauth_token_expires_at?: Date;
|
||||
};
|
||||
|
||||
export type MCP = {
|
||||
mcp_id: string;
|
||||
metadata: MCPMetadata;
|
||||
} & ({ assistant_id: string; agent_id?: never } | { assistant_id?: never; agent_id: string });
|
||||
|
||||
export type MCPMetadata = Omit<ActionMetadata, 'auth'> & {
|
||||
name?: string;
|
||||
description?: string;
|
||||
url?: string;
|
||||
tools?: string[];
|
||||
auth?: MCPAuth;
|
||||
icon?: string;
|
||||
trust?: boolean;
|
||||
};
|
||||
|
||||
export type MCPAuth = ActionAuth;
|
||||
|
||||
export type AgentToolType = {
|
||||
tool_id: string;
|
||||
metadata: ToolMetadata;
|
||||
} & ({ assistant_id: string; agent_id?: never } | { assistant_id?: never; agent_id: string });
|
||||
|
||||
export type ToolMetadata = TPlugin;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export enum Tools {
|
|||
web_search = 'web_search',
|
||||
retrieval = 'retrieval',
|
||||
function = 'function',
|
||||
memory = 'memory',
|
||||
}
|
||||
|
||||
export enum EToolResources {
|
||||
|
|
@ -486,60 +487,6 @@ export const actionDomainSeparator = '---';
|
|||
export const hostImageIdSuffix = '_host_copy';
|
||||
export const hostImageNamePrefix = 'host_copy_';
|
||||
|
||||
export enum AuthTypeEnum {
|
||||
ServiceHttp = 'service_http',
|
||||
OAuth = 'oauth',
|
||||
None = 'none',
|
||||
}
|
||||
|
||||
export enum AuthorizationTypeEnum {
|
||||
Bearer = 'bearer',
|
||||
Basic = 'basic',
|
||||
Custom = 'custom',
|
||||
}
|
||||
|
||||
export enum TokenExchangeMethodEnum {
|
||||
DefaultPost = 'default_post',
|
||||
BasicAuthHeader = 'basic_auth_header',
|
||||
}
|
||||
|
||||
export type ActionAuth = {
|
||||
authorization_type?: AuthorizationTypeEnum;
|
||||
custom_auth_header?: string;
|
||||
type?: AuthTypeEnum;
|
||||
authorization_content_type?: string;
|
||||
authorization_url?: string;
|
||||
client_url?: string;
|
||||
scope?: string;
|
||||
token_exchange_method?: TokenExchangeMethodEnum;
|
||||
};
|
||||
|
||||
export type ActionMetadata = {
|
||||
api_key?: string;
|
||||
auth?: ActionAuth;
|
||||
domain?: string;
|
||||
privacy_policy_url?: string;
|
||||
raw_spec?: string;
|
||||
oauth_client_id?: string;
|
||||
oauth_client_secret?: string;
|
||||
};
|
||||
|
||||
export type ActionMetadataRuntime = ActionMetadata & {
|
||||
oauth_access_token?: string;
|
||||
oauth_refresh_token?: string;
|
||||
oauth_token_expires_at?: Date;
|
||||
};
|
||||
|
||||
/* Assistant types */
|
||||
|
||||
export type Action = {
|
||||
action_id: string;
|
||||
type?: string;
|
||||
settings?: Record<string, unknown>;
|
||||
metadata: ActionMetadata;
|
||||
version: number | string;
|
||||
} & ({ assistant_id: string; agent_id?: never } | { assistant_id?: never; agent_id: string });
|
||||
|
||||
export type AssistantAvatar = {
|
||||
filepath: string;
|
||||
source: string;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export enum FileSources {
|
|||
vectordb = 'vectordb',
|
||||
execute_code = 'execute_code',
|
||||
mistral_ocr = 'mistral_ocr',
|
||||
azure_mistral_ocr = 'azure_mistral_ocr',
|
||||
text = 'text',
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,13 @@ import {
|
|||
Assistant,
|
||||
AssistantCreateParams,
|
||||
AssistantUpdateParams,
|
||||
ActionMetadata,
|
||||
FunctionTool,
|
||||
AssistantDocument,
|
||||
Action,
|
||||
Agent,
|
||||
AgentCreateParams,
|
||||
AgentUpdateParams,
|
||||
} from './assistants';
|
||||
import { Action, ActionMetadata } from './agents';
|
||||
|
||||
export type MutationOptions<
|
||||
Response,
|
||||
|
|
@ -278,7 +277,7 @@ export type UpdatePermVars<T> = {
|
|||
};
|
||||
|
||||
export type UpdatePromptPermVars = UpdatePermVars<p.TPromptPermissions>;
|
||||
|
||||
export type UpdateMemoryPermVars = UpdatePermVars<p.TMemoryPermissions>;
|
||||
export type UpdateAgentPermVars = UpdatePermVars<p.TAgentPermissions>;
|
||||
|
||||
export type UpdatePermResponse = r.TRole;
|
||||
|
|
@ -290,6 +289,13 @@ export type UpdatePromptPermOptions = MutationOptions<
|
|||
types.TError | null | undefined
|
||||
>;
|
||||
|
||||
export type UpdateMemoryPermOptions = MutationOptions<
|
||||
UpdatePermResponse,
|
||||
UpdateMemoryPermVars,
|
||||
unknown,
|
||||
types.TError | null | undefined
|
||||
>;
|
||||
|
||||
export type UpdateAgentPermOptions = MutationOptions<
|
||||
UpdatePermResponse,
|
||||
UpdateAgentPermVars,
|
||||
|
|
|
|||
|
|
@ -109,3 +109,18 @@ export type VerifyToolAuthResponse = {
|
|||
|
||||
export type GetToolCallParams = { conversationId: string };
|
||||
export type ToolCallResults = a.ToolCallResult[];
|
||||
|
||||
/* Memories */
|
||||
export type TUserMemory = {
|
||||
key: string;
|
||||
value: string;
|
||||
updated_at: string;
|
||||
tokenCount?: number;
|
||||
};
|
||||
|
||||
export type MemoriesResponse = {
|
||||
memories: TUserMemory[];
|
||||
totalTokens: number;
|
||||
tokenLimit: number | null;
|
||||
usagePercentage: number | null;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import type {
|
|||
SearchProviders,
|
||||
TWebSearchConfig,
|
||||
} from './config';
|
||||
import { extractVariableName } from './utils';
|
||||
import { SearchCategories, SafeSearchTypes } from './config';
|
||||
import { extractVariableName } from './utils';
|
||||
import { AuthType } from './schemas';
|
||||
|
||||
export function loadWebSearchConfig(
|
||||
|
|
@ -64,23 +64,29 @@ export const webSearchAuth = {
|
|||
/**
|
||||
* Extracts all API keys from the webSearchAuth configuration object
|
||||
*/
|
||||
export const webSearchKeys: TWebSearchKeys[] = [];
|
||||
export function getWebSearchKeys(): TWebSearchKeys[] {
|
||||
const keys: TWebSearchKeys[] = [];
|
||||
|
||||
// Iterate through each category (providers, scrapers, rerankers)
|
||||
for (const category of Object.keys(webSearchAuth)) {
|
||||
const categoryObj = webSearchAuth[category as TWebSearchCategories];
|
||||
// Iterate through each category (providers, scrapers, rerankers)
|
||||
for (const category of Object.keys(webSearchAuth)) {
|
||||
const categoryObj = webSearchAuth[category as TWebSearchCategories];
|
||||
|
||||
// Iterate through each service within the category
|
||||
for (const service of Object.keys(categoryObj)) {
|
||||
const serviceObj = categoryObj[service as keyof typeof categoryObj];
|
||||
// Iterate through each service within the category
|
||||
for (const service of Object.keys(categoryObj)) {
|
||||
const serviceObj = categoryObj[service as keyof typeof categoryObj];
|
||||
|
||||
// Extract the API keys from the service
|
||||
for (const key of Object.keys(serviceObj)) {
|
||||
webSearchKeys.push(key as TWebSearchKeys);
|
||||
// Extract the API keys from the service
|
||||
for (const key of Object.keys(serviceObj)) {
|
||||
keys.push(key as TWebSearchKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
export const webSearchKeys: TWebSearchKeys[] = getWebSearchKeys();
|
||||
|
||||
export function extractWebSearchEnvVars({
|
||||
keys,
|
||||
config,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue