mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-26 04:06:12 +01:00
Merge branch 'main' into feat/Multitenant-login-OIDC
This commit is contained in:
commit
c14751cef5
417 changed files with 28394 additions and 9012 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "librechat-data-provider",
|
||||
"version": "0.7.6991",
|
||||
"version": "0.7.73",
|
||||
"description": "data services for librechat apps",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.es.js",
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
},
|
||||
"homepage": "https://librechat.ai",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.7",
|
||||
"axios": "^1.8.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export default {
|
|||
input: entryPath,
|
||||
output: {
|
||||
dir: 'test_bundle',
|
||||
format: 'cjs',
|
||||
format: 'es',
|
||||
},
|
||||
plugins: [
|
||||
alias(customAliases),
|
||||
|
|
|
|||
|
|
@ -585,21 +585,99 @@ describe('resolveRef', () => {
|
|||
openapiSpec.paths['/ai.chatgpt.render-flowchart']?.post
|
||||
?.requestBody as OpenAPIV3.RequestBodyObject
|
||||
).content['application/json'].schema;
|
||||
expect(flowchartRequestRef).toBeDefined();
|
||||
const resolvedFlowchartRequest = resolveRef(
|
||||
flowchartRequestRef as OpenAPIV3.RequestBodyObject,
|
||||
openapiSpec.components,
|
||||
);
|
||||
|
||||
expect(resolvedFlowchartRequest).toBeDefined();
|
||||
expect(resolvedFlowchartRequest.type).toBe('object');
|
||||
const properties = resolvedFlowchartRequest.properties as FlowchartSchema;
|
||||
expect(properties).toBeDefined();
|
||||
expect(flowchartRequestRef).toBeDefined();
|
||||
|
||||
const resolvedSchemaObject = resolveRef(
|
||||
flowchartRequestRef as OpenAPIV3.ReferenceObject,
|
||||
openapiSpec.components,
|
||||
) as OpenAPIV3.SchemaObject;
|
||||
|
||||
expect(resolvedSchemaObject).toBeDefined();
|
||||
expect(resolvedSchemaObject.type).toBe('object');
|
||||
expect(resolvedSchemaObject.properties).toBeDefined();
|
||||
|
||||
const properties = resolvedSchemaObject.properties as FlowchartSchema;
|
||||
expect(properties.mermaid).toBeDefined();
|
||||
expect(properties.mermaid.type).toBe('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveRef general cases', () => {
|
||||
const spec = {
|
||||
openapi: '3.0.0',
|
||||
info: { title: 'TestSpec', version: '1.0.0' },
|
||||
paths: {},
|
||||
components: {
|
||||
schemas: {
|
||||
TestSchema: { type: 'string' },
|
||||
},
|
||||
parameters: {
|
||||
TestParam: {
|
||||
name: 'myParam',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { $ref: '#/components/schemas/TestSchema' },
|
||||
},
|
||||
},
|
||||
requestBodies: {
|
||||
TestRequestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: { $ref: '#/components/schemas/TestSchema' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies OpenAPIV3.Document;
|
||||
|
||||
it('resolves schema refs correctly', () => {
|
||||
const schemaRef: OpenAPIV3.ReferenceObject = { $ref: '#/components/schemas/TestSchema' };
|
||||
const resolvedSchema = resolveRef<OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject>(
|
||||
schemaRef,
|
||||
spec.components,
|
||||
);
|
||||
expect(resolvedSchema.type).toEqual('string');
|
||||
});
|
||||
|
||||
it('resolves parameter refs correctly, then schema within parameter', () => {
|
||||
const paramRef: OpenAPIV3.ReferenceObject = { $ref: '#/components/parameters/TestParam' };
|
||||
const resolvedParam = resolveRef<OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject>(
|
||||
paramRef,
|
||||
spec.components,
|
||||
);
|
||||
expect(resolvedParam.name).toEqual('myParam');
|
||||
expect(resolvedParam.in).toEqual('query');
|
||||
expect(resolvedParam.required).toBe(false);
|
||||
|
||||
const paramSchema = resolveRef<OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject>(
|
||||
resolvedParam.schema as OpenAPIV3.ReferenceObject,
|
||||
spec.components,
|
||||
);
|
||||
expect(paramSchema.type).toEqual('string');
|
||||
});
|
||||
|
||||
it('resolves requestBody refs correctly, then schema within requestBody', () => {
|
||||
const requestBodyRef: OpenAPIV3.ReferenceObject = {
|
||||
$ref: '#/components/requestBodies/TestRequestBody',
|
||||
};
|
||||
const resolvedRequestBody = resolveRef<OpenAPIV3.ReferenceObject | OpenAPIV3.RequestBodyObject>(
|
||||
requestBodyRef,
|
||||
spec.components,
|
||||
);
|
||||
|
||||
expect(resolvedRequestBody.content['application/json']).toBeDefined();
|
||||
|
||||
const schemaInRequestBody = resolveRef<OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject>(
|
||||
resolvedRequestBody.content['application/json'].schema as OpenAPIV3.ReferenceObject,
|
||||
spec.components,
|
||||
);
|
||||
|
||||
expect(schemaInRequestBody.type).toEqual('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('openapiToFunction', () => {
|
||||
it('converts OpenAPI spec to function signatures and request builders', () => {
|
||||
const { functionSignatures, requestBuilders } = openapiToFunction(getWeatherOpenapiSpec);
|
||||
|
|
@ -1095,4 +1173,43 @@ describe('createURL', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('openapiToFunction parameter refs resolution', () => {
|
||||
const weatherSpec = {
|
||||
openapi: '3.0.0',
|
||||
info: { title: 'Weather', version: '1.0.0' },
|
||||
servers: [{ url: 'https://api.weather.gov' }],
|
||||
paths: {
|
||||
'/points/{point}': {
|
||||
get: {
|
||||
operationId: 'getPoint',
|
||||
parameters: [{ $ref: '#/components/parameters/PathPoint' }],
|
||||
responses: { '200': { description: 'ok' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
parameters: {
|
||||
PathPoint: {
|
||||
name: 'point',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: { type: 'string', pattern: '^(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)$' },
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies OpenAPIV3.Document;
|
||||
|
||||
it('correctly resolves $ref for parameters', () => {
|
||||
const { functionSignatures } = openapiToFunction(weatherSpec, true);
|
||||
const func = functionSignatures.find((sig) => sig.name === 'getPoint');
|
||||
expect(func).toBeDefined();
|
||||
expect(func?.parameters.properties).toHaveProperty('point');
|
||||
expect(func?.parameters.required).toContain('point');
|
||||
|
||||
const paramSchema = func?.parameters.properties['point'] as OpenAPIV3.SchemaObject;
|
||||
expect(paramSchema.type).toEqual('string');
|
||||
expect(paramSchema.pattern).toEqual('^(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)$');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
52
packages/data-provider/specs/mcp.spec.ts
Normal file
52
packages/data-provider/specs/mcp.spec.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { StdioOptionsSchema } from '../src/mcp';
|
||||
|
||||
describe('Environment Variable Extraction (MCP)', () => {
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {
|
||||
...originalEnv,
|
||||
TEST_API_KEY: 'test-api-key-value',
|
||||
ANOTHER_SECRET: 'another-secret-value',
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
describe('StdioOptionsSchema', () => {
|
||||
it('should transform environment variables in the env field', () => {
|
||||
const options = {
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
env: {
|
||||
API_KEY: '${TEST_API_KEY}',
|
||||
ANOTHER_KEY: '${ANOTHER_SECRET}',
|
||||
PLAIN_VALUE: 'plain-value',
|
||||
NON_EXISTENT: '${NON_EXISTENT_VAR}',
|
||||
},
|
||||
};
|
||||
|
||||
const result = StdioOptionsSchema.parse(options);
|
||||
|
||||
expect(result.env).toEqual({
|
||||
API_KEY: 'test-api-key-value',
|
||||
ANOTHER_KEY: 'another-secret-value',
|
||||
PLAIN_VALUE: 'plain-value',
|
||||
NON_EXISTENT: '${NON_EXISTENT_VAR}',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle undefined env field', () => {
|
||||
const options = {
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
};
|
||||
|
||||
const result = StdioOptionsSchema.parse(options);
|
||||
|
||||
expect(result.env).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import { extractEnvVariable } from '../src/parsers';
|
||||
|
||||
describe('extractEnvVariable', () => {
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
process.env = { ...originalEnv };
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
test('should return the value of the environment variable', () => {
|
||||
process.env.TEST_VAR = 'test_value';
|
||||
expect(extractEnvVariable('${TEST_VAR}')).toBe('test_value');
|
||||
});
|
||||
|
||||
test('should return the original string if the envrionment variable is not defined correctly', () => {
|
||||
process.env.TEST_VAR = 'test_value';
|
||||
expect(extractEnvVariable('${ TEST_VAR }')).toBe('${ TEST_VAR }');
|
||||
});
|
||||
|
||||
test('should return the original string if environment variable is not set', () => {
|
||||
expect(extractEnvVariable('${NON_EXISTENT_VAR}')).toBe('${NON_EXISTENT_VAR}');
|
||||
});
|
||||
|
||||
test('should return the original string if it does not contain an environment variable', () => {
|
||||
expect(extractEnvVariable('some_string')).toBe('some_string');
|
||||
});
|
||||
|
||||
test('should handle empty strings', () => {
|
||||
expect(extractEnvVariable('')).toBe('');
|
||||
});
|
||||
|
||||
test('should handle strings without variable format', () => {
|
||||
expect(extractEnvVariable('no_var_here')).toBe('no_var_here');
|
||||
});
|
||||
|
||||
test('should not process multiple variable formats', () => {
|
||||
process.env.FIRST_VAR = 'first';
|
||||
process.env.SECOND_VAR = 'second';
|
||||
expect(extractEnvVariable('${FIRST_VAR} and ${SECOND_VAR}')).toBe(
|
||||
'${FIRST_VAR} and ${SECOND_VAR}',
|
||||
);
|
||||
});
|
||||
});
|
||||
129
packages/data-provider/specs/utils.spec.ts
Normal file
129
packages/data-provider/specs/utils.spec.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import { extractEnvVariable } from '../src/utils';
|
||||
|
||||
describe('Environment Variable Extraction', () => {
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {
|
||||
...originalEnv,
|
||||
TEST_API_KEY: 'test-api-key-value',
|
||||
ANOTHER_SECRET: 'another-secret-value',
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
describe('extractEnvVariable (original tests)', () => {
|
||||
test('should return the value of the environment variable', () => {
|
||||
process.env.TEST_VAR = 'test_value';
|
||||
expect(extractEnvVariable('${TEST_VAR}')).toBe('test_value');
|
||||
});
|
||||
|
||||
test('should return the original string if the envrionment variable is not defined correctly', () => {
|
||||
process.env.TEST_VAR = 'test_value';
|
||||
expect(extractEnvVariable('${ TEST_VAR }')).toBe('${ TEST_VAR }');
|
||||
});
|
||||
|
||||
test('should return the original string if environment variable is not set', () => {
|
||||
expect(extractEnvVariable('${NON_EXISTENT_VAR}')).toBe('${NON_EXISTENT_VAR}');
|
||||
});
|
||||
|
||||
test('should return the original string if it does not contain an environment variable', () => {
|
||||
expect(extractEnvVariable('some_string')).toBe('some_string');
|
||||
});
|
||||
|
||||
test('should handle empty strings', () => {
|
||||
expect(extractEnvVariable('')).toBe('');
|
||||
});
|
||||
|
||||
test('should handle strings without variable format', () => {
|
||||
expect(extractEnvVariable('no_var_here')).toBe('no_var_here');
|
||||
});
|
||||
|
||||
/** No longer the expected behavior; keeping for reference */
|
||||
test.skip('should not process multiple variable formats', () => {
|
||||
process.env.FIRST_VAR = 'first';
|
||||
process.env.SECOND_VAR = 'second';
|
||||
expect(extractEnvVariable('${FIRST_VAR} and ${SECOND_VAR}')).toBe(
|
||||
'${FIRST_VAR} and ${SECOND_VAR}',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractEnvVariable function', () => {
|
||||
it('should extract environment variables from exact matches', () => {
|
||||
expect(extractEnvVariable('${TEST_API_KEY}')).toBe('test-api-key-value');
|
||||
expect(extractEnvVariable('${ANOTHER_SECRET}')).toBe('another-secret-value');
|
||||
});
|
||||
|
||||
it('should extract environment variables from strings with prefixes', () => {
|
||||
expect(extractEnvVariable('prefix-${TEST_API_KEY}')).toBe('prefix-test-api-key-value');
|
||||
});
|
||||
|
||||
it('should extract environment variables from strings with suffixes', () => {
|
||||
expect(extractEnvVariable('${TEST_API_KEY}-suffix')).toBe('test-api-key-value-suffix');
|
||||
});
|
||||
|
||||
it('should extract environment variables from strings with both prefixes and suffixes', () => {
|
||||
expect(extractEnvVariable('prefix-${TEST_API_KEY}-suffix')).toBe(
|
||||
'prefix-test-api-key-value-suffix',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not match invalid patterns', () => {
|
||||
expect(extractEnvVariable('$TEST_API_KEY')).toBe('$TEST_API_KEY');
|
||||
expect(extractEnvVariable('{TEST_API_KEY}')).toBe('{TEST_API_KEY}');
|
||||
expect(extractEnvVariable('TEST_API_KEY')).toBe('TEST_API_KEY');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractEnvVariable', () => {
|
||||
it('should extract environment variable values', () => {
|
||||
expect(extractEnvVariable('${TEST_API_KEY}')).toBe('test-api-key-value');
|
||||
expect(extractEnvVariable('${ANOTHER_SECRET}')).toBe('another-secret-value');
|
||||
});
|
||||
|
||||
it('should return the original string if environment variable is not found', () => {
|
||||
expect(extractEnvVariable('${NON_EXISTENT_VAR}')).toBe('${NON_EXISTENT_VAR}');
|
||||
});
|
||||
|
||||
it('should return the original string if no environment variable pattern is found', () => {
|
||||
expect(extractEnvVariable('plain-string')).toBe('plain-string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractEnvVariable space trimming', () => {
|
||||
beforeEach(() => {
|
||||
process.env.HELLO = 'world';
|
||||
process.env.USER = 'testuser';
|
||||
});
|
||||
|
||||
it('should extract the value when string contains only an environment variable with surrounding whitespace', () => {
|
||||
expect(extractEnvVariable(' ${HELLO} ')).toBe('world');
|
||||
expect(extractEnvVariable(' ${HELLO} ')).toBe('world');
|
||||
expect(extractEnvVariable('\t${HELLO}\n')).toBe('world');
|
||||
});
|
||||
|
||||
it('should preserve content when variable is part of a larger string', () => {
|
||||
expect(extractEnvVariable('Hello ${USER}!')).toBe('Hello testuser!');
|
||||
expect(extractEnvVariable(' Hello ${USER}! ')).toBe('Hello testuser!');
|
||||
});
|
||||
|
||||
it('should not handle multiple variables', () => {
|
||||
expect(extractEnvVariable('${HELLO} ${USER}')).toBe('${HELLO} ${USER}');
|
||||
expect(extractEnvVariable(' ${HELLO} ${USER} ')).toBe('${HELLO} ${USER}');
|
||||
});
|
||||
|
||||
it('should handle undefined variables', () => {
|
||||
expect(extractEnvVariable(' ${UNDEFINED_VAR} ')).toBe('${UNDEFINED_VAR}');
|
||||
});
|
||||
|
||||
it('should handle mixed content correctly', () => {
|
||||
expect(extractEnvVariable('Welcome, ${USER}!\nYour message: ${HELLO}')).toBe(
|
||||
'Welcome, testuser!\nYour message: world',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -22,8 +22,8 @@ export type ParametersSchema = {
|
|||
|
||||
export type OpenAPISchema = OpenAPIV3.SchemaObject &
|
||||
ParametersSchema & {
|
||||
items?: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject;
|
||||
};
|
||||
items?: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject;
|
||||
};
|
||||
|
||||
export type ApiKeyCredentials = {
|
||||
api_key: string;
|
||||
|
|
@ -43,8 +43,8 @@ export type Credentials = ApiKeyCredentials | OAuthCredentials;
|
|||
type MediaTypeObject =
|
||||
| undefined
|
||||
| {
|
||||
[media: string]: OpenAPIV3.MediaTypeObject | undefined;
|
||||
};
|
||||
[media: string]: OpenAPIV3.MediaTypeObject | undefined;
|
||||
};
|
||||
|
||||
type RequestBodyObject = Omit<OpenAPIV3.RequestBodyObject, 'content'> & {
|
||||
content: MediaTypeObject;
|
||||
|
|
@ -358,19 +358,29 @@ export class ActionRequest {
|
|||
}
|
||||
}
|
||||
|
||||
export function resolveRef(
|
||||
schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | RequestBodyObject,
|
||||
components?: OpenAPIV3.ComponentsObject,
|
||||
): OpenAPIV3.SchemaObject {
|
||||
if ('$ref' in schema && components) {
|
||||
const refPath = schema.$ref.replace(/^#\/components\/schemas\//, '');
|
||||
const resolvedSchema = components.schemas?.[refPath];
|
||||
if (!resolvedSchema) {
|
||||
throw new Error(`Reference ${schema.$ref} not found`);
|
||||
export function resolveRef<
|
||||
T extends
|
||||
| OpenAPIV3.ReferenceObject
|
||||
| OpenAPIV3.SchemaObject
|
||||
| OpenAPIV3.ParameterObject
|
||||
| OpenAPIV3.RequestBodyObject,
|
||||
>(obj: T, components?: OpenAPIV3.ComponentsObject): Exclude<T, OpenAPIV3.ReferenceObject> {
|
||||
if ('$ref' in obj && components) {
|
||||
const refPath = obj.$ref.replace(/^#\/components\//, '').split('/');
|
||||
|
||||
let resolved: unknown = components as Record<string, unknown>;
|
||||
for (const segment of refPath) {
|
||||
if (typeof resolved === 'object' && resolved !== null && segment in resolved) {
|
||||
resolved = (resolved as Record<string, unknown>)[segment];
|
||||
} else {
|
||||
throw new Error(`Could not resolve reference: ${obj.$ref}`);
|
||||
}
|
||||
}
|
||||
return resolveRef(resolvedSchema, components);
|
||||
|
||||
return resolveRef(resolved as typeof obj, components) as Exclude<T, OpenAPIV3.ReferenceObject>;
|
||||
}
|
||||
return schema as OpenAPIV3.SchemaObject;
|
||||
|
||||
return obj as Exclude<T, OpenAPIV3.ReferenceObject>;
|
||||
}
|
||||
|
||||
function sanitizeOperationId(input: string) {
|
||||
|
|
@ -399,7 +409,7 @@ export function openapiToFunction(
|
|||
const operationObj = operation as OpenAPIV3.OperationObject & {
|
||||
'x-openai-isConsequential'?: boolean;
|
||||
} & {
|
||||
'x-strict'?: boolean
|
||||
'x-strict'?: boolean;
|
||||
};
|
||||
|
||||
// Operation ID is used as the function name
|
||||
|
|
@ -415,15 +425,25 @@ export function openapiToFunction(
|
|||
};
|
||||
|
||||
if (operationObj.parameters) {
|
||||
for (const param of operationObj.parameters) {
|
||||
const paramObj = param as OpenAPIV3.ParameterObject;
|
||||
const resolvedSchema = resolveRef(
|
||||
{ ...paramObj.schema } as OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject,
|
||||
for (const param of operationObj.parameters ?? []) {
|
||||
const resolvedParam = resolveRef(
|
||||
param,
|
||||
openapiSpec.components,
|
||||
);
|
||||
parametersSchema.properties[paramObj.name] = resolvedSchema;
|
||||
if (paramObj.required === true) {
|
||||
parametersSchema.required.push(paramObj.name);
|
||||
) as OpenAPIV3.ParameterObject;
|
||||
|
||||
const paramName = resolvedParam.name;
|
||||
if (!paramName || !resolvedParam.schema) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const paramSchema = resolveRef(
|
||||
resolvedParam.schema,
|
||||
openapiSpec.components,
|
||||
) as OpenAPIV3.SchemaObject;
|
||||
|
||||
parametersSchema.properties[paramName] = paramSchema;
|
||||
if (resolvedParam.required) {
|
||||
parametersSchema.required.push(paramName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -446,7 +466,12 @@ export function openapiToFunction(
|
|||
}
|
||||
}
|
||||
|
||||
const functionSignature = new FunctionSignature(operationId, description, parametersSchema, isStrict);
|
||||
const functionSignature = new FunctionSignature(
|
||||
operationId,
|
||||
description,
|
||||
parametersSchema,
|
||||
isStrict,
|
||||
);
|
||||
functionSignatures.push(functionSignature);
|
||||
|
||||
const actionRequest = new ActionRequest(
|
||||
|
|
@ -544,4 +569,4 @@ export function validateAndParseOpenAPISpec(specString: string): ValidationResul
|
|||
console.error(error);
|
||||
return { status: false, message: 'Error parsing OpenAPI spec.' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,3 +237,11 @@ export const addTagToConversation = (conversationId: string) =>
|
|||
export const userTerms = () => '/api/user/terms';
|
||||
export const acceptUserTerms = () => '/api/user/terms/accept';
|
||||
export const banner = () => '/api/banner';
|
||||
|
||||
// Two-Factor Endpoints
|
||||
export const enableTwoFactor = () => '/api/auth/2fa/enable';
|
||||
export const verifyTwoFactor = () => '/api/auth/2fa/verify';
|
||||
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';
|
||||
|
|
@ -6,8 +6,9 @@ import type {
|
|||
TValidatedAzureConfig,
|
||||
TAzureConfigValidationResult,
|
||||
} from '../src/config';
|
||||
import { errorsToString, extractEnvVariable, envVarRegex } from '../src/parsers';
|
||||
import { extractEnvVariable, envVarRegex } from '../src/utils';
|
||||
import { azureGroupConfigsSchema } from '../src/config';
|
||||
import { errorsToString } from '../src/parsers';
|
||||
|
||||
export const deprecatedAzureVariables = [
|
||||
/* "related to" precedes description text */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,20 @@
|
|||
import { z } from 'zod';
|
||||
import * as s from './schemas';
|
||||
|
||||
type ThinkingConfig = {
|
||||
type: 'enabled';
|
||||
budget_tokens: number;
|
||||
};
|
||||
type AnthropicReasoning = {
|
||||
thinking?: ThinkingConfig | boolean;
|
||||
thinkingBudget?: number;
|
||||
};
|
||||
|
||||
type AnthropicInput = BedrockConverseInput & {
|
||||
additionalModelRequestFields: BedrockConverseInput['additionalModelRequestFields'] &
|
||||
AnthropicReasoning;
|
||||
};
|
||||
|
||||
export const bedrockInputSchema = s.tConversationSchema
|
||||
.pick({
|
||||
/* LibreChat params; optionType: 'conversation' */
|
||||
|
|
@ -21,11 +35,24 @@ export const bedrockInputSchema = s.tConversationSchema
|
|||
temperature: true,
|
||||
topP: true,
|
||||
stop: true,
|
||||
thinking: true,
|
||||
thinkingBudget: true,
|
||||
/* Catch-all fields */
|
||||
topK: true,
|
||||
additionalModelRequestFields: true,
|
||||
})
|
||||
.transform((obj) => s.removeNullishValues(obj))
|
||||
.transform((obj) => {
|
||||
if ((obj as AnthropicInput).additionalModelRequestFields?.thinking != null) {
|
||||
const _obj = obj as AnthropicInput;
|
||||
obj.thinking = !!_obj.additionalModelRequestFields.thinking;
|
||||
obj.thinkingBudget =
|
||||
typeof _obj.additionalModelRequestFields.thinking === 'object'
|
||||
? (_obj.additionalModelRequestFields.thinking as ThinkingConfig)?.budget_tokens
|
||||
: undefined;
|
||||
delete obj.additionalModelRequestFields;
|
||||
}
|
||||
return s.removeNullishValues(obj);
|
||||
})
|
||||
.catch(() => ({}));
|
||||
|
||||
export type BedrockConverseInput = z.infer<typeof bedrockInputSchema>;
|
||||
|
|
@ -49,6 +76,8 @@ export const bedrockInputParser = s.tConversationSchema
|
|||
temperature: true,
|
||||
topP: true,
|
||||
stop: true,
|
||||
thinking: true,
|
||||
thinkingBudget: true,
|
||||
/* Catch-all fields */
|
||||
topK: true,
|
||||
additionalModelRequestFields: true,
|
||||
|
|
@ -87,6 +116,27 @@ export const bedrockInputParser = s.tConversationSchema
|
|||
}
|
||||
});
|
||||
|
||||
/** Default thinking and thinkingBudget for 'anthropic.claude-3-7-sonnet' models, if not defined */
|
||||
if (
|
||||
typeof typedData.model === 'string' &&
|
||||
typedData.model.includes('anthropic.claude-3-7-sonnet')
|
||||
) {
|
||||
if (additionalFields.thinking === undefined) {
|
||||
additionalFields.thinking = true;
|
||||
} else if (additionalFields.thinking === false) {
|
||||
delete additionalFields.thinking;
|
||||
delete additionalFields.thinkingBudget;
|
||||
}
|
||||
|
||||
if (additionalFields.thinking === true && additionalFields.thinkingBudget === undefined) {
|
||||
additionalFields.thinkingBudget = 2000;
|
||||
}
|
||||
additionalFields.anthropic_beta = ['output-128k-2025-02-19'];
|
||||
} else if (additionalFields.thinking != null || additionalFields.thinkingBudget != null) {
|
||||
delete additionalFields.thinking;
|
||||
delete additionalFields.thinkingBudget;
|
||||
}
|
||||
|
||||
if (Object.keys(additionalFields).length > 0) {
|
||||
typedData.additionalModelRequestFields = {
|
||||
...((typedData.additionalModelRequestFields as Record<string, unknown> | undefined) || {}),
|
||||
|
|
@ -104,9 +154,34 @@ export const bedrockInputParser = s.tConversationSchema
|
|||
})
|
||||
.catch(() => ({}));
|
||||
|
||||
/**
|
||||
* Configures the "thinking" parameter based on given input and thinking options.
|
||||
*
|
||||
* @param data - The parsed Bedrock request options object
|
||||
* @returns The object with thinking configured appropriately
|
||||
*/
|
||||
function configureThinking(data: AnthropicInput): AnthropicInput {
|
||||
const updatedData = { ...data };
|
||||
if (updatedData.additionalModelRequestFields?.thinking === true) {
|
||||
updatedData.maxTokens = updatedData.maxTokens ?? updatedData.maxOutputTokens ?? 8192;
|
||||
delete updatedData.maxOutputTokens;
|
||||
const thinkingConfig: AnthropicReasoning['thinking'] = {
|
||||
type: 'enabled',
|
||||
budget_tokens: updatedData.additionalModelRequestFields.thinkingBudget ?? 2000,
|
||||
};
|
||||
|
||||
if (thinkingConfig.budget_tokens > updatedData.maxTokens) {
|
||||
thinkingConfig.budget_tokens = Math.floor(updatedData.maxTokens * 0.9);
|
||||
}
|
||||
updatedData.additionalModelRequestFields.thinking = thinkingConfig;
|
||||
delete updatedData.additionalModelRequestFields.thinkingBudget;
|
||||
}
|
||||
return updatedData;
|
||||
}
|
||||
|
||||
export const bedrockOutputParser = (data: Record<string, unknown>) => {
|
||||
const knownKeys = [...Object.keys(s.tConversationSchema.shape), 'topK', 'top_k'];
|
||||
const result: Record<string, unknown> = {};
|
||||
let result: Record<string, unknown> = {};
|
||||
|
||||
// Extract known fields from the root level
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
|
|
@ -125,6 +200,8 @@ export const bedrockOutputParser = (data: Record<string, unknown>) => {
|
|||
if (knownKeys.includes(key)) {
|
||||
if (key === 'top_k') {
|
||||
result['topK'] = value;
|
||||
} else if (key === 'thinking' || key === 'thinkingBudget') {
|
||||
return;
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
|
|
@ -140,8 +217,11 @@ export const bedrockOutputParser = (data: Record<string, unknown>) => {
|
|||
result.maxTokens = result.maxOutputTokens;
|
||||
}
|
||||
|
||||
// Remove additionalModelRequestFields from the result
|
||||
delete result.additionalModelRequestFields;
|
||||
result = configureThinking(result as AnthropicInput);
|
||||
// Remove additionalModelRequestFields from the result if it doesn't thinking config
|
||||
if ((result as AnthropicInput).additionalModelRequestFields?.thinking == null) {
|
||||
delete result.additionalModelRequestFields;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { z } from 'zod';
|
|||
import type { ZodError } from 'zod';
|
||||
import type { TModelsConfig } from './types';
|
||||
import { EModelEndpoint, eModelEndpointSchema } from './schemas';
|
||||
import { fileConfigSchema } from './file-config';
|
||||
import { specsConfigSchema, TSpecsConfig } from './models';
|
||||
import { fileConfigSchema } from './file-config';
|
||||
import { FileSources } from './types/files';
|
||||
import { MCPServersSchema } from './mcp';
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ export const defaultRetrievalModels = [
|
|||
'o1-preview',
|
||||
'o1-mini-2024-09-12',
|
||||
'o1-mini',
|
||||
'o3-mini',
|
||||
'chatgpt-4o-latest',
|
||||
'gpt-4o-2024-05-13',
|
||||
'gpt-4o-2024-08-06',
|
||||
|
|
@ -31,6 +32,27 @@ export const defaultRetrievalModels = [
|
|||
'gpt-4-1106',
|
||||
];
|
||||
|
||||
export const excludedKeys = new Set([
|
||||
'conversationId',
|
||||
'title',
|
||||
'iconURL',
|
||||
'greeting',
|
||||
'endpoint',
|
||||
'endpointType',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'expiredAt',
|
||||
'messages',
|
||||
'isArchived',
|
||||
'tags',
|
||||
'user',
|
||||
'__v',
|
||||
'_id',
|
||||
'tools',
|
||||
'model',
|
||||
'files',
|
||||
]);
|
||||
|
||||
export enum SettingsViews {
|
||||
default = 'default',
|
||||
advanced = 'advanced',
|
||||
|
|
@ -146,6 +168,8 @@ export enum AgentCapabilities {
|
|||
artifacts = 'artifacts',
|
||||
actions = 'actions',
|
||||
tools = 'tools',
|
||||
chain = 'chain',
|
||||
ocr = 'ocr',
|
||||
}
|
||||
|
||||
export const defaultAssistantsVersion = {
|
||||
|
|
@ -211,6 +235,7 @@ export const agentsEndpointSChema = baseEndpointSchema.merge(
|
|||
/* agents specific */
|
||||
recursionLimit: z.number().optional(),
|
||||
disableBuilder: z.boolean().optional(),
|
||||
maxRecursionLimit: z.number().optional(),
|
||||
capabilities: z
|
||||
.array(z.nativeEnum(AgentCapabilities))
|
||||
.optional()
|
||||
|
|
@ -220,6 +245,8 @@ export const agentsEndpointSChema = baseEndpointSchema.merge(
|
|||
AgentCapabilities.artifacts,
|
||||
AgentCapabilities.actions,
|
||||
AgentCapabilities.tools,
|
||||
AgentCapabilities.ocr,
|
||||
AgentCapabilities.chain,
|
||||
]),
|
||||
}),
|
||||
);
|
||||
|
|
@ -446,6 +473,7 @@ export const intefaceSchema = z
|
|||
})
|
||||
.optional(),
|
||||
termsOfService: termsOfServiceSchema.optional(),
|
||||
customWelcome: z.string().optional(),
|
||||
endpointsMenu: z.boolean().optional(),
|
||||
modelSelect: z.boolean().optional(),
|
||||
parameters: z.boolean().optional(),
|
||||
|
|
@ -456,6 +484,7 @@ export const intefaceSchema = z
|
|||
prompts: z.boolean().optional(),
|
||||
agents: z.boolean().optional(),
|
||||
temporaryChat: z.boolean().optional(),
|
||||
runCode: z.boolean().optional(),
|
||||
})
|
||||
.default({
|
||||
endpointsMenu: true,
|
||||
|
|
@ -468,6 +497,7 @@ export const intefaceSchema = z
|
|||
prompts: true,
|
||||
agents: true,
|
||||
temporaryChat: true,
|
||||
runCode: true,
|
||||
});
|
||||
|
||||
export type TInterfaceConfig = z.infer<typeof intefaceSchema>;
|
||||
|
|
@ -485,6 +515,7 @@ export type TStartupConfig = {
|
|||
appleLoginEnabled: boolean;
|
||||
openidLabel: string;
|
||||
openidImageUrl: string;
|
||||
openidAutoRedirect: boolean;
|
||||
/** LDAP Auth Configuration */
|
||||
ldap?: {
|
||||
/** LDAP enabled */
|
||||
|
|
@ -507,11 +538,25 @@ export type TStartupConfig = {
|
|||
publicSharedLinksEnabled: boolean;
|
||||
analyticsGtmId?: string;
|
||||
instanceProjectId: string;
|
||||
bundlerURL?: string;
|
||||
};
|
||||
|
||||
export enum OCRStrategy {
|
||||
MISTRAL_OCR = 'mistral_ocr',
|
||||
CUSTOM_OCR = 'custom_ocr',
|
||||
}
|
||||
|
||||
export const ocrSchema = z.object({
|
||||
mistralModel: z.string().optional(),
|
||||
apiKey: z.string().optional().default('OCR_API_KEY'),
|
||||
baseURL: z.string().optional().default('OCR_BASEURL'),
|
||||
strategy: z.nativeEnum(OCRStrategy).default(OCRStrategy.MISTRAL_OCR),
|
||||
});
|
||||
|
||||
export const configSchema = z.object({
|
||||
version: z.string(),
|
||||
cache: z.boolean().default(true),
|
||||
ocr: ocrSchema.optional(),
|
||||
secureImageLinks: z.boolean().optional(),
|
||||
imageOutputType: z.nativeEnum(EImageOutputType).default(EImageOutputType.PNG),
|
||||
includedTools: z.array(z.string()).optional(),
|
||||
|
|
@ -639,12 +684,15 @@ export const alternateName = {
|
|||
[EModelEndpoint.custom]: 'Custom',
|
||||
[EModelEndpoint.bedrock]: 'AWS Bedrock',
|
||||
[KnownEndpoints.ollama]: 'Ollama',
|
||||
[KnownEndpoints.deepseek]: 'DeepSeek',
|
||||
[KnownEndpoints.xai]: 'xAI',
|
||||
};
|
||||
|
||||
const sharedOpenAIModels = [
|
||||
'gpt-4o-mini',
|
||||
'gpt-4o',
|
||||
'gpt-4.5-preview',
|
||||
'gpt-4.5-preview-2025-02-27',
|
||||
'gpt-3.5-turbo',
|
||||
'gpt-3.5-turbo-0125',
|
||||
'gpt-4-turbo',
|
||||
|
|
@ -663,6 +711,8 @@ const sharedOpenAIModels = [
|
|||
];
|
||||
|
||||
const sharedAnthropicModels = [
|
||||
'claude-3-7-sonnet-latest',
|
||||
'claude-3-7-sonnet-20250219',
|
||||
'claude-3-5-haiku-20241022',
|
||||
'claude-3-5-sonnet-20241022',
|
||||
'claude-3-5-sonnet-20240620',
|
||||
|
|
@ -715,14 +765,14 @@ export const bedrockModels = [
|
|||
|
||||
export const defaultModels = {
|
||||
[EModelEndpoint.azureAssistants]: sharedOpenAIModels,
|
||||
[EModelEndpoint.assistants]: ['chatgpt-4o-latest', ...sharedOpenAIModels],
|
||||
[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',
|
||||
'gemini-2.0-flash-lite-preview-02-05',
|
||||
'gemini-2.0-flash-lite',
|
||||
'gemini-2.0-pro-exp-02-05',
|
||||
// Gemini 1.5 Models
|
||||
'gemini-1.5-flash-001',
|
||||
|
|
@ -734,8 +784,8 @@ export const defaultModels = {
|
|||
],
|
||||
[EModelEndpoint.anthropic]: sharedAnthropicModels,
|
||||
[EModelEndpoint.openAI]: [
|
||||
'chatgpt-4o-latest',
|
||||
...sharedOpenAIModels,
|
||||
'chatgpt-4o-latest',
|
||||
'gpt-4-vision-preview',
|
||||
'gpt-3.5-turbo-instruct-0914',
|
||||
'gpt-3.5-turbo-instruct',
|
||||
|
|
@ -800,24 +850,29 @@ export const supportsBalanceCheck = {
|
|||
};
|
||||
|
||||
export const visionModels = [
|
||||
'gpt-4o',
|
||||
'qwen-vl',
|
||||
'grok-vision',
|
||||
'grok-2-vision',
|
||||
'grok-3',
|
||||
'gpt-4o-mini',
|
||||
'o1',
|
||||
'gpt-4o',
|
||||
'gpt-4-turbo',
|
||||
'gpt-4-vision',
|
||||
'o1',
|
||||
'gpt-4.5',
|
||||
'llava',
|
||||
'llava-13b',
|
||||
'gemini-pro-vision',
|
||||
'claude-3',
|
||||
'gemini-2.0',
|
||||
'gemini-1.5',
|
||||
'gemini-exp',
|
||||
'gemini-1.5',
|
||||
'gemini-2.0',
|
||||
'moondream',
|
||||
'llama3.2-vision',
|
||||
'llama-3.2-90b-vision',
|
||||
'llama-3.2-11b-vision',
|
||||
'llama-3-2-90b-vision',
|
||||
'llama-3-2-11b-vision',
|
||||
'llama-3.2-90b-vision',
|
||||
'llama-3-2-90b-vision',
|
||||
];
|
||||
export enum VisionModes {
|
||||
generative = 'generative',
|
||||
|
|
@ -848,7 +903,7 @@ export function validateVisionModel({
|
|||
return visionModels.concat(additionalModels).some((visionModel) => model.includes(visionModel));
|
||||
}
|
||||
|
||||
export const imageGenTools = new Set(['dalle', 'dall-e', 'stable-diffusion']);
|
||||
export const imageGenTools = new Set(['dalle', 'dall-e', 'stable-diffusion', 'flux']);
|
||||
|
||||
/**
|
||||
* Enum for collections using infinite queries
|
||||
|
|
@ -1157,9 +1212,9 @@ export enum TTSProviders {
|
|||
/** Enum for app-wide constants */
|
||||
export enum Constants {
|
||||
/** Key for the app's version. */
|
||||
VERSION = 'v0.7.7-rc1',
|
||||
VERSION = 'v0.7.7',
|
||||
/** Key for the Custom Config's version (librechat.yaml). */
|
||||
CONFIG_VERSION = '1.2.1',
|
||||
CONFIG_VERSION = '1.2.3',
|
||||
/** 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 */
|
||||
|
|
@ -1227,7 +1282,9 @@ export enum ForkOptions {
|
|||
/** Key for including branches */
|
||||
INCLUDE_BRANCHES = 'includeBranches',
|
||||
/** Key for target level fork (default) */
|
||||
TARGET_LEVEL = '',
|
||||
TARGET_LEVEL = 'targetLevel',
|
||||
/** Default option */
|
||||
DEFAULT = 'default',
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -774,3 +774,33 @@ export function acceptTerms(): Promise<t.TAcceptTermsResponse> {
|
|||
export function getBanner(): Promise<t.TBannerResponse> {
|
||||
return request.get(endpoints.banner());
|
||||
}
|
||||
|
||||
export function enableTwoFactor(): Promise<t.TEnable2FAResponse> {
|
||||
return request.get(endpoints.enableTwoFactor());
|
||||
}
|
||||
|
||||
export function verifyTwoFactor(
|
||||
payload: t.TVerify2FARequest,
|
||||
): Promise<t.TVerify2FAResponse> {
|
||||
return request.post(endpoints.verifyTwoFactor(), payload);
|
||||
}
|
||||
|
||||
export function confirmTwoFactor(
|
||||
payload: t.TVerify2FARequest,
|
||||
): Promise<t.TVerify2FAResponse> {
|
||||
return request.post(endpoints.confirmTwoFactor(), payload);
|
||||
}
|
||||
|
||||
export function disableTwoFactor(): Promise<t.TDisable2FAResponse> {
|
||||
return request.post(endpoints.disableTwoFactor());
|
||||
}
|
||||
|
||||
export function regenerateBackupCodes(): Promise<t.TRegenerateBackupCodesResponse> {
|
||||
return request.post(endpoints.regenerateBackupCodes());
|
||||
}
|
||||
|
||||
export function verifyTwoFactorTemp(
|
||||
payload: t.TVerify2FATempRequest,
|
||||
): Promise<t.TVerify2FATempResponse> {
|
||||
return request.post(endpoints.verifyTwoFactorTemp(), payload);
|
||||
}
|
||||
|
|
@ -149,6 +149,9 @@ export const codeTypeMapping: { [key: string]: string } = {
|
|||
ts: 'application/typescript',
|
||||
tar: 'application/x-tar',
|
||||
zip: 'application/zip',
|
||||
yml: 'application/x-yaml',
|
||||
yaml: 'application/x-yaml',
|
||||
log: 'text/plain',
|
||||
};
|
||||
|
||||
export const retrievalMimeTypes = [
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export * from './file-config';
|
|||
export * from './artifacts';
|
||||
/* schema helpers */
|
||||
export * from './parsers';
|
||||
export * from './ocr';
|
||||
export * from './zod';
|
||||
/* custom/dynamic configurations */
|
||||
export * from './generate';
|
||||
|
|
@ -31,5 +32,6 @@ export { default as request } from './request';
|
|||
export { dataService };
|
||||
import * as dataService from './data-service';
|
||||
/* general helpers */
|
||||
export * from './utils';
|
||||
export * from './actions';
|
||||
export { default as createPayload } from './createPayload';
|
||||
|
|
|
|||
|
|
@ -67,4 +67,6 @@ export enum MutationKeys {
|
|||
deleteAgentAction = 'deleteAgentAction',
|
||||
deleteUser = 'deleteUser',
|
||||
updateRole = 'updateRole',
|
||||
enableTwoFactor = 'enableTwoFactor',
|
||||
verifyTwoFactor = 'verifyTwoFactor',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { z } from 'zod';
|
||||
import { extractEnvVariable } from './utils';
|
||||
|
||||
const BaseOptionsSchema = z.object({
|
||||
iconPath: z.string().optional(),
|
||||
timeout: z.number().optional(),
|
||||
initTimeout: z.number().optional(),
|
||||
});
|
||||
|
||||
export const StdioOptionsSchema = BaseOptionsSchema.extend({
|
||||
|
|
@ -18,8 +21,22 @@ export const StdioOptionsSchema = BaseOptionsSchema.extend({
|
|||
* The environment to use when spawning the process.
|
||||
*
|
||||
* If not specified, the result of getDefaultEnvironment() will be used.
|
||||
* Environment variables can be referenced using ${VAR_NAME} syntax.
|
||||
*/
|
||||
env: z.record(z.string(), z.string()).optional(),
|
||||
env: z
|
||||
.record(z.string(), z.string())
|
||||
.optional()
|
||||
.transform((env) => {
|
||||
if (!env) {
|
||||
return env;
|
||||
}
|
||||
|
||||
const processedEnv: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(env)) {
|
||||
processedEnv[key] = extractEnvVariable(value);
|
||||
}
|
||||
return processedEnv;
|
||||
}),
|
||||
/**
|
||||
* How to handle stderr of the child process. This matches the semantics of Node's `child_process.spawn`.
|
||||
*
|
||||
|
|
@ -69,3 +86,26 @@ export const MCPOptionsSchema = z.union([
|
|||
]);
|
||||
|
||||
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
|
||||
* @returns {MCPOptions} - The processed object with environment variables replaced
|
||||
*/
|
||||
export function processMCPEnv(obj: MCPOptions): MCPOptions {
|
||||
if (obj === null || obj === undefined) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if ('env' in obj && obj.env) {
|
||||
const processedEnv: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(obj.env)) {
|
||||
processedEnv[key] = extractEnvVariable(value);
|
||||
}
|
||||
obj.env = processedEnv;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
|
|
|||
14
packages/data-provider/src/ocr.ts
Normal file
14
packages/data-provider/src/ocr.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import type { TCustomConfig } from '../src/config';
|
||||
import { OCRStrategy } from '../src/config';
|
||||
|
||||
export function loadOCRConfig(config: TCustomConfig['ocr']): TCustomConfig['ocr'] {
|
||||
const baseURL = config?.baseURL ?? '';
|
||||
const apiKey = config?.apiKey ?? '';
|
||||
const mistralModel = config?.mistralModel ?? '';
|
||||
return {
|
||||
apiKey,
|
||||
baseURL,
|
||||
mistralModel,
|
||||
strategy: config?.strategy ?? OCRStrategy.MISTRAL_OCR,
|
||||
};
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import {
|
|||
compactAssistantSchema,
|
||||
} from './schemas';
|
||||
import { bedrockInputSchema } from './bedrock';
|
||||
import { extractEnvVariable } from './utils';
|
||||
import { alternateName } from './config';
|
||||
|
||||
type EndpointSchema =
|
||||
|
|
@ -122,18 +123,6 @@ export function errorsToString(errors: ZodIssue[]) {
|
|||
.join(' ');
|
||||
}
|
||||
|
||||
export const envVarRegex = /^\${(.+)}$/;
|
||||
|
||||
/** Extracts the value of an environment variable from a string. */
|
||||
export function extractEnvVariable(value: string) {
|
||||
const envVarMatch = value.match(envVarRegex);
|
||||
if (envVarMatch) {
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
return process.env[envVarMatch[1]] || value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Resolves header values to env variables if detected */
|
||||
export function resolveHeaders(headers: Record<string, string> | undefined) {
|
||||
const resolvedHeaders = { ...(headers ?? {}) };
|
||||
|
|
@ -211,6 +200,29 @@ export const parseConvo = ({
|
|||
return convo;
|
||||
};
|
||||
|
||||
/** Match GPT followed by digit, optional decimal, and optional suffix
|
||||
*
|
||||
* Examples: gpt-4, gpt-4o, gpt-4.5, gpt-5a, etc. */
|
||||
const extractGPTVersion = (modelStr: string): string => {
|
||||
const gptMatch = modelStr.match(/gpt-(\d+(?:\.\d+)?)([a-z])?/i);
|
||||
if (gptMatch) {
|
||||
const version = gptMatch[1];
|
||||
const suffix = gptMatch[2] || '';
|
||||
return `GPT-${version}${suffix}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/** Match omni models (o1, o3, etc.), "o" followed by a digit, possibly with decimal */
|
||||
const extractOmniVersion = (modelStr: string): string => {
|
||||
const omniMatch = modelStr.match(/\bo(\d+(?:\.\d+)?)\b/i);
|
||||
if (omniMatch) {
|
||||
const version = omniMatch[1];
|
||||
return `o${version}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const getResponseSender = (endpointOption: t.TEndpointOption): string => {
|
||||
const {
|
||||
model: _m,
|
||||
|
|
@ -238,18 +250,13 @@ export const getResponseSender = (endpointOption: t.TEndpointOption): string =>
|
|||
return chatGptLabel;
|
||||
} else if (modelLabel) {
|
||||
return modelLabel;
|
||||
} else if (model && /\bo1\b/i.test(model)) {
|
||||
return 'o1';
|
||||
} else if (model && /\bo3\b/i.test(model)) {
|
||||
return 'o3';
|
||||
} else if (model && model.includes('gpt-3')) {
|
||||
return 'GPT-3.5';
|
||||
} else if (model && model.includes('gpt-4o')) {
|
||||
return 'GPT-4o';
|
||||
} else if (model && model.includes('gpt-4')) {
|
||||
return 'GPT-4';
|
||||
} else if (model && extractOmniVersion(model)) {
|
||||
return extractOmniVersion(model);
|
||||
} else if (model && model.includes('mistral')) {
|
||||
return 'Mistral';
|
||||
} else if (model && model.includes('gpt-')) {
|
||||
const gptVersion = extractGPTVersion(model);
|
||||
return gptVersion || 'GPT';
|
||||
}
|
||||
return (alternateName[endpoint] as string | undefined) ?? 'ChatGPT';
|
||||
}
|
||||
|
|
@ -279,14 +286,13 @@ export const getResponseSender = (endpointOption: t.TEndpointOption): string =>
|
|||
return modelLabel;
|
||||
} else if (chatGptLabel) {
|
||||
return chatGptLabel;
|
||||
} else if (model && extractOmniVersion(model)) {
|
||||
return extractOmniVersion(model);
|
||||
} else if (model && model.includes('mistral')) {
|
||||
return 'Mistral';
|
||||
} else if (model && model.includes('gpt-3')) {
|
||||
return 'GPT-3.5';
|
||||
} else if (model && model.includes('gpt-4o')) {
|
||||
return 'GPT-4o';
|
||||
} else if (model && model.includes('gpt-4')) {
|
||||
return 'GPT-4';
|
||||
} else if (model && model.includes('gpt-')) {
|
||||
const gptVersion = extractGPTVersion(model);
|
||||
return gptVersion || 'GPT';
|
||||
} else if (modelDisplayLabel) {
|
||||
return modelDisplayLabel;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ axios.interceptors.response.use(
|
|||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (originalRequest.url?.includes('/api/auth/2fa') === true) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
if (originalRequest.url?.includes('/api/auth/logout') === true) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,14 @@ export enum PermissionTypes {
|
|||
* Type for Multi-Conversation Permissions
|
||||
*/
|
||||
MULTI_CONVO = 'MULTI_CONVO',
|
||||
/**
|
||||
* Type for Temporary Chat
|
||||
*/
|
||||
TEMPORARY_CHAT = 'TEMPORARY_CHAT',
|
||||
/**
|
||||
* Type for using the "Run Code" LC Code Interpreter API feature
|
||||
*/
|
||||
RUN_CODE = 'RUN_CODE',
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,7 +76,15 @@ export const agentPermissionsSchema = z.object({
|
|||
});
|
||||
|
||||
export const multiConvoPermissionsSchema = z.object({
|
||||
[Permissions.USE]: z.boolean().default(false),
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const temporaryChatPermissionsSchema = z.object({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const runCodePermissionsSchema = z.object({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const roleSchema = z.object({
|
||||
|
|
@ -77,6 +93,8 @@ export const roleSchema = z.object({
|
|||
[PermissionTypes.BOOKMARKS]: bookmarkPermissionsSchema,
|
||||
[PermissionTypes.AGENTS]: agentPermissionsSchema,
|
||||
[PermissionTypes.MULTI_CONVO]: multiConvoPermissionsSchema,
|
||||
[PermissionTypes.TEMPORARY_CHAT]: temporaryChatPermissionsSchema,
|
||||
[PermissionTypes.RUN_CODE]: runCodePermissionsSchema,
|
||||
});
|
||||
|
||||
export type TRole = z.infer<typeof roleSchema>;
|
||||
|
|
@ -84,6 +102,8 @@ export type TAgentPermissions = z.infer<typeof agentPermissionsSchema>;
|
|||
export type TPromptPermissions = z.infer<typeof promptPermissionsSchema>;
|
||||
export type TBookmarkPermissions = z.infer<typeof bookmarkPermissionsSchema>;
|
||||
export type TMultiConvoPermissions = z.infer<typeof multiConvoPermissionsSchema>;
|
||||
export type TTemporaryChatPermissions = z.infer<typeof temporaryChatPermissionsSchema>;
|
||||
export type TRunCodePermissions = z.infer<typeof runCodePermissionsSchema>;
|
||||
|
||||
const defaultRolesSchema = z.object({
|
||||
[SystemRoles.ADMIN]: roleSchema.extend({
|
||||
|
|
@ -106,6 +126,12 @@ const defaultRolesSchema = z.object({
|
|||
[PermissionTypes.MULTI_CONVO]: multiConvoPermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.TEMPORARY_CHAT]: temporaryChatPermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.RUN_CODE]: runCodePermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
}),
|
||||
}),
|
||||
[SystemRoles.USER]: roleSchema.extend({
|
||||
name: z.literal(SystemRoles.USER),
|
||||
|
|
@ -113,6 +139,8 @@ const defaultRolesSchema = z.object({
|
|||
[PermissionTypes.BOOKMARKS]: bookmarkPermissionsSchema,
|
||||
[PermissionTypes.AGENTS]: agentPermissionsSchema,
|
||||
[PermissionTypes.MULTI_CONVO]: multiConvoPermissionsSchema,
|
||||
[PermissionTypes.TEMPORARY_CHAT]: temporaryChatPermissionsSchema,
|
||||
[PermissionTypes.RUN_CODE]: runCodePermissionsSchema,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -123,6 +151,8 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[PermissionTypes.BOOKMARKS]: {},
|
||||
[PermissionTypes.AGENTS]: {},
|
||||
[PermissionTypes.MULTI_CONVO]: {},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: {},
|
||||
[PermissionTypes.RUN_CODE]: {},
|
||||
},
|
||||
[SystemRoles.USER]: {
|
||||
name: SystemRoles.USER,
|
||||
|
|
@ -130,5 +160,7 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[PermissionTypes.BOOKMARKS]: {},
|
||||
[PermissionTypes.AGENTS]: {},
|
||||
[PermissionTypes.MULTI_CONVO]: {},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: {},
|
||||
[PermissionTypes.RUN_CODE]: {},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export enum BedrockProviders {
|
|||
Meta = 'meta',
|
||||
MistralAI = 'mistral',
|
||||
StabilityAI = 'stability',
|
||||
DeepSeek = 'deepseek',
|
||||
}
|
||||
|
||||
export const getModelKey = (endpoint: EModelEndpoint | string, model: string) => {
|
||||
|
|
@ -157,6 +158,7 @@ export const defaultAgentFormValues = {
|
|||
projectIds: [],
|
||||
artifacts: '',
|
||||
isCollaborative: false,
|
||||
recursion_limit: undefined,
|
||||
[Tools.execute_code]: false,
|
||||
[Tools.file_search]: false,
|
||||
};
|
||||
|
|
@ -179,34 +181,34 @@ export const isImageVisionTool = (tool: FunctionTool | FunctionToolCall) =>
|
|||
|
||||
export const openAISettings = {
|
||||
model: {
|
||||
default: 'gpt-4o',
|
||||
default: 'gpt-4o-mini' as const,
|
||||
},
|
||||
temperature: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
default: 1,
|
||||
min: 0 as const,
|
||||
max: 2 as const,
|
||||
step: 0.01 as const,
|
||||
default: 1 as const,
|
||||
},
|
||||
top_p: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 1,
|
||||
min: 0 as const,
|
||||
max: 1 as const,
|
||||
step: 0.01 as const,
|
||||
default: 1 as const,
|
||||
},
|
||||
presence_penalty: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
default: 0,
|
||||
min: 0 as const,
|
||||
max: 2 as const,
|
||||
step: 0.01 as const,
|
||||
default: 0 as const,
|
||||
},
|
||||
frequency_penalty: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
default: 0,
|
||||
min: 0 as const,
|
||||
max: 2 as const,
|
||||
step: 0.01 as const,
|
||||
default: 0 as const,
|
||||
},
|
||||
resendFiles: {
|
||||
default: true,
|
||||
default: true as const,
|
||||
},
|
||||
maxContextTokens: {
|
||||
default: undefined,
|
||||
|
|
@ -215,72 +217,85 @@ export const openAISettings = {
|
|||
default: undefined,
|
||||
},
|
||||
imageDetail: {
|
||||
default: ImageDetail.auto,
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 1,
|
||||
default: ImageDetail.auto as const,
|
||||
min: 0 as const,
|
||||
max: 2 as const,
|
||||
step: 1 as const,
|
||||
},
|
||||
};
|
||||
|
||||
export const googleSettings = {
|
||||
model: {
|
||||
default: 'gemini-1.5-flash-latest',
|
||||
default: 'gemini-1.5-flash-latest' as const,
|
||||
},
|
||||
maxOutputTokens: {
|
||||
min: 1,
|
||||
max: 8192,
|
||||
step: 1,
|
||||
default: 8192,
|
||||
min: 1 as const,
|
||||
max: 8192 as const,
|
||||
step: 1 as const,
|
||||
default: 8192 as const,
|
||||
},
|
||||
temperature: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
default: 1,
|
||||
min: 0 as const,
|
||||
max: 2 as const,
|
||||
step: 0.01 as const,
|
||||
default: 1 as const,
|
||||
},
|
||||
topP: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.95,
|
||||
min: 0 as const,
|
||||
max: 1 as const,
|
||||
step: 0.01 as const,
|
||||
default: 0.95 as const,
|
||||
},
|
||||
topK: {
|
||||
min: 1,
|
||||
max: 40,
|
||||
step: 1,
|
||||
default: 40,
|
||||
min: 1 as const,
|
||||
max: 40 as const,
|
||||
step: 1 as const,
|
||||
default: 40 as const,
|
||||
},
|
||||
};
|
||||
|
||||
const ANTHROPIC_MAX_OUTPUT = 8192;
|
||||
const LEGACY_ANTHROPIC_MAX_OUTPUT = 4096;
|
||||
const ANTHROPIC_MAX_OUTPUT = 128000 as const;
|
||||
const DEFAULT_MAX_OUTPUT = 8192 as const;
|
||||
const LEGACY_ANTHROPIC_MAX_OUTPUT = 4096 as const;
|
||||
export const anthropicSettings = {
|
||||
model: {
|
||||
default: 'claude-3-5-sonnet-20241022',
|
||||
default: 'claude-3-5-sonnet-latest' as const,
|
||||
},
|
||||
temperature: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 1,
|
||||
min: 0 as const,
|
||||
max: 1 as const,
|
||||
step: 0.01 as const,
|
||||
default: 1 as const,
|
||||
},
|
||||
promptCache: {
|
||||
default: true,
|
||||
default: true as const,
|
||||
},
|
||||
thinking: {
|
||||
default: true as const,
|
||||
},
|
||||
thinkingBudget: {
|
||||
min: 1024 as const,
|
||||
step: 100 as const,
|
||||
max: 200000 as const,
|
||||
default: 2000 as const,
|
||||
},
|
||||
maxOutputTokens: {
|
||||
min: 1,
|
||||
min: 1 as const,
|
||||
max: ANTHROPIC_MAX_OUTPUT,
|
||||
step: 1,
|
||||
default: ANTHROPIC_MAX_OUTPUT,
|
||||
step: 1 as const,
|
||||
default: DEFAULT_MAX_OUTPUT,
|
||||
reset: (modelName: string) => {
|
||||
if (modelName.includes('claude-3-5-sonnet')) {
|
||||
return ANTHROPIC_MAX_OUTPUT;
|
||||
if (/claude-3[-.]5-sonnet/.test(modelName) || /claude-3[-.]7/.test(modelName)) {
|
||||
return DEFAULT_MAX_OUTPUT;
|
||||
}
|
||||
|
||||
return 4096;
|
||||
},
|
||||
set: (value: number, modelName: string) => {
|
||||
if (!modelName.includes('claude-3-5-sonnet') && value > LEGACY_ANTHROPIC_MAX_OUTPUT) {
|
||||
if (
|
||||
!(/claude-3[-.]5-sonnet/.test(modelName) || /claude-3[-.]7/.test(modelName)) &&
|
||||
value > LEGACY_ANTHROPIC_MAX_OUTPUT
|
||||
) {
|
||||
return LEGACY_ANTHROPIC_MAX_OUTPUT;
|
||||
}
|
||||
|
||||
|
|
@ -288,28 +303,28 @@ export const anthropicSettings = {
|
|||
},
|
||||
},
|
||||
topP: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.7,
|
||||
min: 0 as const,
|
||||
max: 1 as const,
|
||||
step: 0.01 as const,
|
||||
default: 0.7 as const,
|
||||
},
|
||||
topK: {
|
||||
min: 1,
|
||||
max: 40,
|
||||
step: 1,
|
||||
default: 5,
|
||||
min: 1 as const,
|
||||
max: 40 as const,
|
||||
step: 1 as const,
|
||||
default: 5 as const,
|
||||
},
|
||||
resendFiles: {
|
||||
default: true,
|
||||
default: true as const,
|
||||
},
|
||||
maxContextTokens: {
|
||||
default: undefined,
|
||||
},
|
||||
legacy: {
|
||||
maxOutputTokens: {
|
||||
min: 1,
|
||||
min: 1 as const,
|
||||
max: LEGACY_ANTHROPIC_MAX_OUTPUT,
|
||||
step: 1,
|
||||
step: 1 as const,
|
||||
default: LEGACY_ANTHROPIC_MAX_OUTPUT,
|
||||
},
|
||||
},
|
||||
|
|
@ -317,34 +332,34 @@ export const anthropicSettings = {
|
|||
|
||||
export const agentsSettings = {
|
||||
model: {
|
||||
default: 'gpt-3.5-turbo-test',
|
||||
default: 'gpt-3.5-turbo-test' as const,
|
||||
},
|
||||
temperature: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 1,
|
||||
min: 0 as const,
|
||||
max: 1 as const,
|
||||
step: 0.01 as const,
|
||||
default: 1 as const,
|
||||
},
|
||||
top_p: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 1,
|
||||
min: 0 as const,
|
||||
max: 1 as const,
|
||||
step: 0.01 as const,
|
||||
default: 1 as const,
|
||||
},
|
||||
presence_penalty: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
default: 0,
|
||||
min: 0 as const,
|
||||
max: 2 as const,
|
||||
step: 0.01 as const,
|
||||
default: 0 as const,
|
||||
},
|
||||
frequency_penalty: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
default: 0,
|
||||
min: 0 as const,
|
||||
max: 2 as const,
|
||||
step: 0.01 as const,
|
||||
default: 0 as const,
|
||||
},
|
||||
resendFiles: {
|
||||
default: true,
|
||||
default: true as const,
|
||||
},
|
||||
maxContextTokens: {
|
||||
default: undefined,
|
||||
|
|
@ -353,7 +368,7 @@ export const agentsSettings = {
|
|||
default: undefined,
|
||||
},
|
||||
imageDetail: {
|
||||
default: ImageDetail.auto,
|
||||
default: ImageDetail.auto as const,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -556,6 +571,8 @@ export const tConversationSchema = z.object({
|
|||
/* Anthropic */
|
||||
promptCache: z.boolean().optional(),
|
||||
system: z.string().optional(),
|
||||
thinking: z.boolean().optional(),
|
||||
thinkingBudget: coerceNumber.optional(),
|
||||
/* artifacts */
|
||||
artifacts: z.string().optional(),
|
||||
/* google */
|
||||
|
|
@ -672,6 +689,8 @@ export const tQueryParamsSchema = tConversationSchema
|
|||
maxOutputTokens: true,
|
||||
/** @endpoints anthropic */
|
||||
promptCache: true,
|
||||
thinking: true,
|
||||
thinkingBudget: true,
|
||||
/** @endpoints bedrock */
|
||||
region: true,
|
||||
/** @endpoints bedrock */
|
||||
|
|
@ -747,37 +766,8 @@ export const googleSchema = tConversationSchema
|
|||
spec: true,
|
||||
maxContextTokens: true,
|
||||
})
|
||||
.transform((obj) => {
|
||||
return {
|
||||
...obj,
|
||||
model: obj.model ?? google.model.default,
|
||||
modelLabel: obj.modelLabel ?? null,
|
||||
promptPrefix: obj.promptPrefix ?? null,
|
||||
examples: obj.examples ?? [{ input: { content: '' }, output: { content: '' } }],
|
||||
temperature: obj.temperature ?? google.temperature.default,
|
||||
maxOutputTokens: obj.maxOutputTokens ?? google.maxOutputTokens.default,
|
||||
topP: obj.topP ?? google.topP.default,
|
||||
topK: obj.topK ?? google.topK.default,
|
||||
iconURL: obj.iconURL ?? undefined,
|
||||
greeting: obj.greeting ?? undefined,
|
||||
spec: obj.spec ?? undefined,
|
||||
maxContextTokens: obj.maxContextTokens ?? undefined,
|
||||
};
|
||||
})
|
||||
.catch(() => ({
|
||||
model: google.model.default,
|
||||
modelLabel: null,
|
||||
promptPrefix: null,
|
||||
examples: [{ input: { content: '' }, output: { content: '' } }],
|
||||
temperature: google.temperature.default,
|
||||
maxOutputTokens: google.maxOutputTokens.default,
|
||||
topP: google.topP.default,
|
||||
topK: google.topK.default,
|
||||
iconURL: undefined,
|
||||
greeting: undefined,
|
||||
spec: undefined,
|
||||
maxContextTokens: undefined,
|
||||
}));
|
||||
.transform((obj: Partial<TConversation>) => removeNullishValues(obj))
|
||||
.catch(() => ({}));
|
||||
|
||||
/**
|
||||
* TODO: Map the following fields:
|
||||
|
|
@ -1067,6 +1057,8 @@ export const anthropicSchema = tConversationSchema
|
|||
topK: true,
|
||||
resendFiles: true,
|
||||
promptCache: true,
|
||||
thinking: true,
|
||||
thinkingBudget: true,
|
||||
artifacts: true,
|
||||
iconURL: true,
|
||||
greeting: true,
|
||||
|
|
@ -1162,7 +1154,6 @@ export const compactAgentsSchema = tConversationSchema
|
|||
iconURL: true,
|
||||
greeting: true,
|
||||
agent_id: true,
|
||||
resendFiles: true,
|
||||
instructions: true,
|
||||
additional_instructions: true,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ export type TUpdateUserPlugins = {
|
|||
auth?: unknown;
|
||||
};
|
||||
|
||||
// TODO `label` needs to be changed to the proper `TranslationKeys`
|
||||
export type TCategory = {
|
||||
id?: string;
|
||||
value: string;
|
||||
|
|
@ -99,6 +100,12 @@ export type TError = {
|
|||
};
|
||||
};
|
||||
|
||||
export type TBackupCode = {
|
||||
codeHash: string;
|
||||
used: boolean;
|
||||
usedAt: Date | null;
|
||||
};
|
||||
|
||||
export type TUser = {
|
||||
id: string;
|
||||
username: string;
|
||||
|
|
@ -108,6 +115,8 @@ export type TUser = {
|
|||
role: string;
|
||||
provider: string;
|
||||
plugins?: string[];
|
||||
twoFactorEnabled?: boolean;
|
||||
backupCodes?: TBackupCode[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
|
@ -284,11 +293,61 @@ export type TRegisterUser = {
|
|||
export type TLoginUser = {
|
||||
email: string;
|
||||
password: string;
|
||||
token?: string;
|
||||
backupCode?: string;
|
||||
};
|
||||
|
||||
export type TLoginResponse = {
|
||||
token: string;
|
||||
user: TUser;
|
||||
token?: string;
|
||||
user?: TUser;
|
||||
twoFAPending?: boolean;
|
||||
tempToken?: string;
|
||||
};
|
||||
|
||||
export type TEnable2FAResponse = {
|
||||
otpauthUrl: string;
|
||||
backupCodes: string[];
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export type TVerify2FARequest = {
|
||||
token?: string;
|
||||
backupCode?: string;
|
||||
};
|
||||
|
||||
export type TVerify2FAResponse = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* For verifying 2FA during login with a temporary token.
|
||||
*/
|
||||
export type TVerify2FATempRequest = {
|
||||
tempToken: string;
|
||||
token?: string;
|
||||
backupCode?: string;
|
||||
};
|
||||
|
||||
export type TVerify2FATempResponse = {
|
||||
token?: string;
|
||||
user?: TUser;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Response from disabling 2FA.
|
||||
*/
|
||||
export type TDisable2FAResponse = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Response from regenerating backup codes.
|
||||
*/
|
||||
export type TRegenerateBackupCodesResponse = {
|
||||
message: string;
|
||||
backupCodes: string[];
|
||||
backupCodesHash: string[];
|
||||
};
|
||||
|
||||
export type TRequestPasswordReset = {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@ export namespace Agents {
|
|||
tool_call_ids?: string[];
|
||||
};
|
||||
|
||||
export type AgentUpdate = {
|
||||
type: ContentTypes.AGENT_UPDATE;
|
||||
agent_update: {
|
||||
index: number;
|
||||
runId: string;
|
||||
agentId: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type MessageContentImageUrl = {
|
||||
type: ContentTypes.IMAGE_URL;
|
||||
image_url: string | { url: string; detail?: ImageDetail };
|
||||
|
|
@ -26,6 +35,7 @@ export namespace Agents {
|
|||
|
||||
export type MessageContentComplex =
|
||||
| ReasoningContentText
|
||||
| AgentUpdate
|
||||
| MessageContentText
|
||||
| MessageContentImageUrl
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -159,12 +169,7 @@ export namespace Agents {
|
|||
index: number; // #new
|
||||
stepIndex?: number; // #new
|
||||
stepDetails: StepDetails;
|
||||
usage: null | {
|
||||
// Define usage structure if it's ever non-null
|
||||
// prompt_tokens: number; // #new
|
||||
// completion_tokens: number; // #new
|
||||
// total_tokens: number; // #new
|
||||
};
|
||||
usage: null | object;
|
||||
};
|
||||
/**
|
||||
* Represents a run step delta i.e. any changed fields on a run step during
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export enum EToolResources {
|
|||
code_interpreter = 'code_interpreter',
|
||||
execute_code = 'execute_code',
|
||||
file_search = 'file_search',
|
||||
ocr = 'ocr',
|
||||
}
|
||||
|
||||
export type Tool = {
|
||||
|
|
@ -163,7 +164,8 @@ export type AgentModelParameters = {
|
|||
|
||||
export interface AgentToolResources {
|
||||
execute_code?: ExecuteCodeResource;
|
||||
file_search?: AgentFileSearchResource;
|
||||
file_search?: AgentFileResource;
|
||||
ocr?: Omit<AgentFileResource, 'vector_store_ids'>;
|
||||
}
|
||||
export interface ExecuteCodeResource {
|
||||
/**
|
||||
|
|
@ -177,7 +179,7 @@ export interface ExecuteCodeResource {
|
|||
files?: Array<TFile>;
|
||||
}
|
||||
|
||||
export interface AgentFileSearchResource {
|
||||
export interface AgentFileResource {
|
||||
/**
|
||||
* The ID of the vector store attached to this agent. There
|
||||
* can be a maximum of 1 vector store attached to the agent.
|
||||
|
|
@ -220,6 +222,7 @@ export type Agent = {
|
|||
end_after_tools?: boolean;
|
||||
hide_sequential_outputs?: boolean;
|
||||
artifacts?: ArtifactModes;
|
||||
recursion_limit?: number;
|
||||
};
|
||||
|
||||
export type TAgentsMap = Record<string, Agent | undefined>;
|
||||
|
|
@ -234,7 +237,10 @@ export type AgentCreateParams = {
|
|||
provider: AgentProvider;
|
||||
model: string | null;
|
||||
model_parameters: AgentModelParameters;
|
||||
} & Pick<Agent, 'agent_ids' | 'end_after_tools' | 'hide_sequential_outputs' | 'artifacts'>;
|
||||
} & Pick<
|
||||
Agent,
|
||||
'agent_ids' | 'end_after_tools' | 'hide_sequential_outputs' | 'artifacts' | 'recursion_limit'
|
||||
>;
|
||||
|
||||
export type AgentUpdateParams = {
|
||||
name?: string | null;
|
||||
|
|
@ -250,7 +256,10 @@ export type AgentUpdateParams = {
|
|||
projectIds?: string[];
|
||||
removeProjectIds?: string[];
|
||||
isCollaborative?: boolean;
|
||||
} & Pick<Agent, 'agent_ids' | 'end_after_tools' | 'hide_sequential_outputs' | 'artifacts'>;
|
||||
} & Pick<
|
||||
Agent,
|
||||
'agent_ids' | 'end_after_tools' | 'hide_sequential_outputs' | 'artifacts' | 'recursion_limit'
|
||||
>;
|
||||
|
||||
export type AgentListParams = {
|
||||
limit?: number;
|
||||
|
|
@ -453,6 +462,7 @@ export type TMessageContentParts =
|
|||
PartMetadata;
|
||||
}
|
||||
| { type: ContentTypes.IMAGE_FILE; image_file: ImageFile & PartMetadata }
|
||||
| Agents.AgentUpdate
|
||||
| Agents.MessageContentImageUrl;
|
||||
|
||||
export type StreamContentData = TMessageContentParts & {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ export enum FileSources {
|
|||
s3 = 's3',
|
||||
vectordb = 'vectordb',
|
||||
execute_code = 'execute_code',
|
||||
mistral_ocr = 'mistral_ocr',
|
||||
text = 'text',
|
||||
}
|
||||
|
||||
export const checkOpenAIStorage = (source: string) =>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export enum ContentTypes {
|
|||
TOOL_CALL = 'tool_call',
|
||||
IMAGE_FILE = 'image_file',
|
||||
IMAGE_URL = 'image_url',
|
||||
AGENT_UPDATE = 'agent_update',
|
||||
ERROR = 'error',
|
||||
}
|
||||
|
||||
|
|
|
|||
44
packages/data-provider/src/utils.ts
Normal file
44
packages/data-provider/src/utils.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
export const envVarRegex = /^\${(.+)}$/;
|
||||
|
||||
/** Extracts the value of an environment variable from a string. */
|
||||
export function extractEnvVariable(value: string) {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Trim the input
|
||||
const trimmed = value.trim();
|
||||
|
||||
// Special case: if it's just a single environment variable
|
||||
const singleMatch = trimmed.match(envVarRegex);
|
||||
if (singleMatch) {
|
||||
const varName = singleMatch[1];
|
||||
return process.env[varName] || trimmed;
|
||||
}
|
||||
|
||||
// For multiple variables, process them using a regex loop
|
||||
const regex = /\${([^}]+)}/g;
|
||||
let result = trimmed;
|
||||
|
||||
// First collect all matches and their positions
|
||||
const matches = [];
|
||||
let match;
|
||||
while ((match = regex.exec(trimmed)) !== null) {
|
||||
matches.push({
|
||||
fullMatch: match[0],
|
||||
varName: match[1],
|
||||
index: match.index,
|
||||
});
|
||||
}
|
||||
|
||||
// Process matches in reverse order to avoid position shifts
|
||||
for (let i = matches.length - 1; i >= 0; i--) {
|
||||
const { fullMatch, varName, index } = matches[i];
|
||||
const envValue = process.env[varName] || fullMatch;
|
||||
|
||||
// Replace at exact position
|
||||
result = result.substring(0, index) + envValue + result.substring(index + fullMatch.length);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
2
packages/data-schemas/.gitignore
vendored
Normal file
2
packages/data-schemas/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
node_modules/
|
||||
test_bundle/
|
||||
21
packages/data-schemas/LICENSE
Normal file
21
packages/data-schemas/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 LibreChat
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
114
packages/data-schemas/README.md
Normal file
114
packages/data-schemas/README.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# `@librechat/data-schemas`
|
||||
|
||||
Mongoose schemas and models for LibreChat. This package provides a comprehensive collection of Mongoose schemas used across the LibreChat project, enabling robust data modeling and validation for various entities such as actions, agents, messages, users, and more.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **Modular Schemas:** Includes schemas for actions, agents, assistants, balance, banners, categories, conversation tags, conversations, files, keys, messages, plugin authentication, presets, projects, prompts, prompt groups, roles, sessions, shared links, tokens, tool calls, transactions, and users.
|
||||
- **TypeScript Support:** Provides TypeScript definitions for type-safe development.
|
||||
- **Ready for Mongoose Integration:** Easily integrate with Mongoose to create models and interact with your MongoDB database.
|
||||
- **Flexible & Extensible:** Designed to support the evolving needs of LibreChat while being adaptable to other projects.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Install the package via npm or yarn:
|
||||
|
||||
```bash
|
||||
npm install @librechat/data-schemas
|
||||
```
|
||||
|
||||
Or with yarn:
|
||||
|
||||
```bash
|
||||
yarn add @librechat/data-schemas
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
After installation, you can import and use the schemas in your project. For example, to create a Mongoose model for a user:
|
||||
|
||||
```js
|
||||
import mongoose from 'mongoose';
|
||||
import { userSchema } from '@librechat/data-schemas';
|
||||
|
||||
const UserModel = mongoose.model('User', userSchema);
|
||||
|
||||
// Now you can use UserModel to create, read, update, and delete user documents.
|
||||
```
|
||||
|
||||
You can also import other schemas as needed:
|
||||
|
||||
```js
|
||||
import { actionSchema, agentSchema, messageSchema } from '@librechat/data-schemas';
|
||||
```
|
||||
|
||||
Each schema is designed to integrate seamlessly with Mongoose and provides indexes, timestamps, and validations tailored for LibreChat’s use cases.
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
This package uses Rollup and TypeScript for building and bundling.
|
||||
|
||||
### Available Scripts
|
||||
|
||||
- **Build:**
|
||||
Cleans the `dist` directory and builds the package.
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
- **Build Watch:**
|
||||
Rebuilds automatically on file changes.
|
||||
```bash
|
||||
npm run build:watch
|
||||
```
|
||||
|
||||
- **Test:**
|
||||
Runs tests with coverage in watch mode.
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
- **Test (CI):**
|
||||
Runs tests with coverage for CI environments.
|
||||
```bash
|
||||
npm run test:ci
|
||||
```
|
||||
|
||||
- **Verify:**
|
||||
Runs tests in CI mode to verify code integrity.
|
||||
```bash
|
||||
npm run verify
|
||||
```
|
||||
|
||||
- **Clean:**
|
||||
Removes the `dist` directory.
|
||||
```bash
|
||||
npm run clean
|
||||
```
|
||||
|
||||
For those using Bun, equivalent scripts are available:
|
||||
- **Bun Clean:** `bun run b:clean`
|
||||
- **Bun Build:** `bun run b:build`
|
||||
|
||||
|
||||
## Repository & Issues
|
||||
|
||||
The source code is maintained on GitHub.
|
||||
- **Repository:** [LibreChat Repository](https://github.com/danny-avila/LibreChat.git)
|
||||
- **Issues & Bug Reports:** [LibreChat Issues](https://github.com/danny-avila/LibreChat/issues)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT License](LICENSE).
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions to improve and expand the data schemas are welcome. If you have suggestions, improvements, or bug fixes, please open an issue or submit a pull request on the [GitHub repository](https://github.com/danny-avila/LibreChat/issues).
|
||||
|
||||
For more detailed documentation on each schema and model, please refer to the source code or visit the [LibreChat website](https://librechat.ai).
|
||||
4
packages/data-schemas/babel.config.cjs
Normal file
4
packages/data-schemas/babel.config.cjs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'],
|
||||
plugins: ['babel-plugin-replace-ts-export-assignment'],
|
||||
};
|
||||
19
packages/data-schemas/jest.config.mjs
Normal file
19
packages/data-schemas/jest.config.mjs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export default {
|
||||
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!<rootDir>/node_modules/'],
|
||||
coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
|
||||
coverageReporters: ['text', 'cobertura'],
|
||||
testResultsProcessor: 'jest-junit',
|
||||
moduleNameMapper: {
|
||||
'^@src/(.*)$': '<rootDir>/src/$1',
|
||||
},
|
||||
// coverageThreshold: {
|
||||
// global: {
|
||||
// statements: 58,
|
||||
// branches: 49,
|
||||
// functions: 50,
|
||||
// lines: 57,
|
||||
// },
|
||||
// },
|
||||
restoreMocks: true,
|
||||
testTimeout: 15000,
|
||||
};
|
||||
77
packages/data-schemas/package.json
Normal file
77
packages/data-schemas/package.json
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"name": "@librechat/data-schemas",
|
||||
"version": "0.0.4",
|
||||
"description": "Mongoose schemas and models for LibreChat",
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.es.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.es.js",
|
||||
"require": "./dist/index.cjs",
|
||||
"types": "./dist/types/index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && rollup -c --silent --bundleConfigAsCjs",
|
||||
"build:watch": "rollup -c -w",
|
||||
"test": "jest --coverage --watch",
|
||||
"test:ci": "jest --coverage --ci",
|
||||
"verify": "npm run test:ci",
|
||||
"b:clean": "bun run rimraf dist",
|
||||
"b:build": "bun run b:clean && bun run rollup -c --silent --bundleConfigAsCjs"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/danny-avila/LibreChat.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/danny-avila/LibreChat/issues"
|
||||
},
|
||||
"homepage": "https://librechat.ai",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-alias": "^5.1.0",
|
||||
"@rollup/plugin-commonjs": "^25.0.2",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.1.0",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/node": "^20.3.0",
|
||||
"jest": "^29.5.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"rimraf": "^5.0.1",
|
||||
"rollup": "^4.22.4",
|
||||
"rollup-plugin-generate-package-json": "^3.2.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||
"rollup-plugin-typescript2": "^0.35.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"mongoose": "^8.12.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"keyv": "^4.5.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"mongoose",
|
||||
"schema",
|
||||
"typescript",
|
||||
"librechat"
|
||||
]
|
||||
}
|
||||
40
packages/data-schemas/rollup.config.js
Normal file
40
packages/data-schemas/rollup.config.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import json from '@rollup/plugin-json';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import nodeResolve from '@rollup/plugin-node-resolve';
|
||||
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
|
||||
|
||||
export default {
|
||||
input: 'src/index.ts',
|
||||
output: [
|
||||
{
|
||||
file: 'dist/index.es.js',
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
file: 'dist/index.cjs',
|
||||
format: 'cjs',
|
||||
sourcemap: true,
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
// Allow importing JSON files
|
||||
json(),
|
||||
// Automatically externalize peer dependencies
|
||||
peerDepsExternal(),
|
||||
// Resolve modules from node_modules
|
||||
nodeResolve(),
|
||||
// Convert CommonJS modules to ES6
|
||||
commonjs(),
|
||||
// Compile TypeScript files and generate type declarations
|
||||
typescript({
|
||||
tsconfig: './tsconfig.json',
|
||||
declaration: true,
|
||||
declarationDir: 'dist/types',
|
||||
rootDir: 'src',
|
||||
}),
|
||||
],
|
||||
// Do not bundle these external dependencies
|
||||
external: ['mongoose'],
|
||||
};
|
||||
68
packages/data-schemas/src/index.ts
Normal file
68
packages/data-schemas/src/index.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
export { default as actionSchema } from './schema/action';
|
||||
export type { IAction } from './schema/action';
|
||||
|
||||
export { default as agentSchema } from './schema/agent';
|
||||
export type { IAgent } from './schema/agent';
|
||||
|
||||
export { default as assistantSchema } from './schema/assistant';
|
||||
export type { IAssistant } from './schema/assistant';
|
||||
|
||||
export { default as balanceSchema } from './schema/balance';
|
||||
export type { IBalance } from './schema/balance';
|
||||
|
||||
export { default as bannerSchema } from './schema/banner';
|
||||
export type { IBanner } from './schema/banner';
|
||||
|
||||
export { default as categoriesSchema } from './schema/categories';
|
||||
export type { ICategory } from './schema/categories';
|
||||
|
||||
export { default as conversationTagSchema } from './schema/conversationTag';
|
||||
export type { IConversationTag } from './schema/conversationTag';
|
||||
|
||||
export { default as convoSchema } from './schema/convo';
|
||||
export type { IConversation } from './schema/convo';
|
||||
|
||||
export { default as fileSchema } from './schema/file';
|
||||
export type { IMongoFile } from './schema/file';
|
||||
|
||||
export { default as keySchema } from './schema/key';
|
||||
export type { IKey } from './schema/key';
|
||||
|
||||
export { default as messageSchema } from './schema/message';
|
||||
export type { IMessage } from './schema/message';
|
||||
|
||||
export { default as pluginAuthSchema } from './schema/pluginAuth';
|
||||
export type { IPluginAuth } from './schema/pluginAuth';
|
||||
|
||||
export { default as presetSchema } from './schema/preset';
|
||||
export type { IPreset } from './schema/preset';
|
||||
|
||||
export { default as projectSchema } from './schema/project';
|
||||
export type { IMongoProject } from './schema/project';
|
||||
|
||||
export { default as promptSchema } from './schema/prompt';
|
||||
export type { IPrompt } from './schema/prompt';
|
||||
|
||||
export { default as promptGroupSchema } from './schema/promptGroup';
|
||||
export type { IPromptGroup, IPromptGroupDocument } from './schema/promptGroup';
|
||||
|
||||
export { default as roleSchema } from './schema/role';
|
||||
export type { IRole } from './schema/role';
|
||||
|
||||
export { default as sessionSchema } from './schema/session';
|
||||
export type { ISession } from './schema/session';
|
||||
|
||||
export { default as shareSchema } from './schema/share';
|
||||
export type { ISharedLink } from './schema/share';
|
||||
|
||||
export { default as tokenSchema } from './schema/token';
|
||||
export type { IToken } from './schema/token';
|
||||
|
||||
export { default as toolCallSchema } from './schema/toolCall';
|
||||
export type { IToolCallData } from './schema/toolCall';
|
||||
|
||||
export { default as transactionSchema } from './schema/transaction';
|
||||
export type { ITransaction } from './schema/transaction';
|
||||
|
||||
export { default as userSchema } from './schema/user';
|
||||
export type { IUser } from './schema/user';
|
||||
78
packages/data-schemas/src/schema/action.ts
Normal file
78
packages/data-schemas/src/schema/action.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import mongoose, { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface IAction extends Document {
|
||||
user: mongoose.Types.ObjectId;
|
||||
action_id: string;
|
||||
type: string;
|
||||
settings?: unknown;
|
||||
agent_id?: string;
|
||||
assistant_id?: string;
|
||||
metadata: {
|
||||
api_key?: string;
|
||||
auth: {
|
||||
authorization_type?: string;
|
||||
custom_auth_header?: string;
|
||||
type: 'service_http' | 'oauth' | 'none';
|
||||
authorization_content_type?: string;
|
||||
authorization_url?: string;
|
||||
client_url?: string;
|
||||
scope?: string;
|
||||
token_exchange_method: 'default_post' | 'basic_auth_header' | null;
|
||||
};
|
||||
domain: string;
|
||||
privacy_policy_url?: string;
|
||||
raw_spec?: string;
|
||||
oauth_client_id?: string;
|
||||
oauth_client_secret?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Define the Auth sub-schema with type-safety.
|
||||
const AuthSchema = new Schema(
|
||||
{
|
||||
authorization_type: { type: String },
|
||||
custom_auth_header: { type: String },
|
||||
type: { type: String, enum: ['service_http', 'oauth', 'none'] },
|
||||
authorization_content_type: { type: String },
|
||||
authorization_url: { type: String },
|
||||
client_url: { type: String },
|
||||
scope: { type: String },
|
||||
token_exchange_method: { type: String, enum: ['default_post', 'basic_auth_header', null] },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const Action = new Schema<IAction>({
|
||||
user: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index: true,
|
||||
required: true,
|
||||
},
|
||||
action_id: {
|
||||
type: String,
|
||||
index: true,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'action_prototype',
|
||||
},
|
||||
settings: Schema.Types.Mixed,
|
||||
agent_id: String,
|
||||
assistant_id: String,
|
||||
metadata: {
|
||||
api_key: String,
|
||||
auth: AuthSchema,
|
||||
domain: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
privacy_policy_url: String,
|
||||
raw_spec: String,
|
||||
oauth_client_id: String,
|
||||
oauth_client_secret: String,
|
||||
},
|
||||
});
|
||||
|
||||
export default Action;
|
||||
124
packages/data-schemas/src/schema/agent.ts
Normal file
124
packages/data-schemas/src/schema/agent.ts
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import { Schema, Document, Types } from 'mongoose';
|
||||
export interface IAgent extends Omit<Document, 'model'> {
|
||||
id: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
instructions?: string;
|
||||
avatar?: {
|
||||
filepath: string;
|
||||
source: string;
|
||||
};
|
||||
provider: string;
|
||||
model: string;
|
||||
model_parameters?: Record<string, unknown>;
|
||||
artifacts?: string;
|
||||
access_level?: number;
|
||||
recursion_limit?: number;
|
||||
tools?: string[];
|
||||
tool_kwargs?: Array<unknown>;
|
||||
actions?: string[];
|
||||
author: Types.ObjectId;
|
||||
authorName?: string;
|
||||
hide_sequential_outputs?: boolean;
|
||||
end_after_tools?: boolean;
|
||||
agent_ids?: string[];
|
||||
isCollaborative?: boolean;
|
||||
conversation_starters?: string[];
|
||||
tool_resources?: unknown;
|
||||
projectIds?: Types.ObjectId[];
|
||||
}
|
||||
|
||||
const agentSchema = new Schema<IAgent>(
|
||||
{
|
||||
id: {
|
||||
type: String,
|
||||
index: true,
|
||||
unique: true,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
instructions: {
|
||||
type: String,
|
||||
},
|
||||
avatar: {
|
||||
type: Schema.Types.Mixed,
|
||||
default: undefined,
|
||||
},
|
||||
provider: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
model: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
model_parameters: {
|
||||
type: Object,
|
||||
},
|
||||
artifacts: {
|
||||
type: String,
|
||||
},
|
||||
access_level: {
|
||||
type: Number,
|
||||
},
|
||||
recursion_limit: {
|
||||
type: Number,
|
||||
},
|
||||
tools: {
|
||||
type: [String],
|
||||
default: undefined,
|
||||
},
|
||||
tool_kwargs: {
|
||||
type: [{ type: Schema.Types.Mixed }],
|
||||
},
|
||||
actions: {
|
||||
type: [String],
|
||||
default: undefined,
|
||||
},
|
||||
author: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
},
|
||||
authorName: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
hide_sequential_outputs: {
|
||||
type: Boolean,
|
||||
},
|
||||
end_after_tools: {
|
||||
type: Boolean,
|
||||
},
|
||||
agent_ids: {
|
||||
type: [String],
|
||||
},
|
||||
isCollaborative: {
|
||||
type: Boolean,
|
||||
default: undefined,
|
||||
},
|
||||
conversation_starters: {
|
||||
type: [String],
|
||||
default: [],
|
||||
},
|
||||
tool_resources: {
|
||||
type: Schema.Types.Mixed,
|
||||
default: {},
|
||||
},
|
||||
projectIds: {
|
||||
type: [Schema.Types.ObjectId],
|
||||
ref: 'Project',
|
||||
index: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
export default agentSchema;
|
||||
52
packages/data-schemas/src/schema/assistant.ts
Normal file
52
packages/data-schemas/src/schema/assistant.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface IAssistant extends Document {
|
||||
user: Types.ObjectId;
|
||||
assistant_id: string;
|
||||
avatar?: {
|
||||
filepath: string;
|
||||
source: string;
|
||||
};
|
||||
conversation_starters?: string[];
|
||||
access_level?: number;
|
||||
file_ids?: string[];
|
||||
actions?: string[];
|
||||
append_current_datetime?: boolean;
|
||||
}
|
||||
|
||||
const assistantSchema = new Schema<IAssistant>(
|
||||
{
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
},
|
||||
assistant_id: {
|
||||
type: String,
|
||||
index: true,
|
||||
required: true,
|
||||
},
|
||||
avatar: {
|
||||
type: Schema.Types.Mixed,
|
||||
default: undefined,
|
||||
},
|
||||
conversation_starters: {
|
||||
type: [String],
|
||||
default: [],
|
||||
},
|
||||
access_level: {
|
||||
type: Number,
|
||||
},
|
||||
file_ids: { type: [String], default: undefined },
|
||||
actions: { type: [String], default: undefined },
|
||||
append_current_datetime: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
export default assistantSchema;
|
||||
22
packages/data-schemas/src/schema/balance.ts
Normal file
22
packages/data-schemas/src/schema/balance.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface IBalance extends Document {
|
||||
user: Types.ObjectId;
|
||||
tokenCredits: number;
|
||||
}
|
||||
|
||||
const balanceSchema = new Schema<IBalance>({
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index: true,
|
||||
required: true,
|
||||
},
|
||||
// 1000 tokenCredits = 1 mill ($0.001 USD)
|
||||
tokenCredits: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export default balanceSchema;
|
||||
43
packages/data-schemas/src/schema/banner.ts
Normal file
43
packages/data-schemas/src/schema/banner.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface IBanner extends Document {
|
||||
bannerId: string;
|
||||
message: string;
|
||||
displayFrom: Date;
|
||||
displayTo?: Date;
|
||||
type: 'banner' | 'popup';
|
||||
isPublic: boolean;
|
||||
}
|
||||
|
||||
const bannerSchema = new Schema<IBanner>(
|
||||
{
|
||||
bannerId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
displayFrom: {
|
||||
type: Date,
|
||||
required: true,
|
||||
default: Date.now,
|
||||
},
|
||||
displayTo: {
|
||||
type: Date,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['banner', 'popup'],
|
||||
default: 'banner',
|
||||
},
|
||||
isPublic: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
export default bannerSchema;
|
||||
21
packages/data-schemas/src/schema/categories.ts
Normal file
21
packages/data-schemas/src/schema/categories.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface ICategory extends Document {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const categoriesSchema = new Schema<ICategory>({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default categoriesSchema;
|
||||
41
packages/data-schemas/src/schema/conversationTag.ts
Normal file
41
packages/data-schemas/src/schema/conversationTag.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface IConversationTag extends Document {
|
||||
tag?: string;
|
||||
user?: string;
|
||||
description?: string;
|
||||
count?: number;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
const conversationTag = new Schema<IConversationTag>(
|
||||
{
|
||||
tag: {
|
||||
type: String,
|
||||
index: true,
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
index: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
index: true,
|
||||
},
|
||||
count: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
position: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
index: true,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
// Create a compound index on tag and user with unique constraint.
|
||||
conversationTag.index({ tag: 1, user: 1 }, { unique: true });
|
||||
|
||||
export default conversationTag;
|
||||
101
packages/data-schemas/src/schema/convo.ts
Normal file
101
packages/data-schemas/src/schema/convo.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
||||
import { conversationPreset } from './defaults';
|
||||
|
||||
// @ts-ignore
|
||||
export interface IConversation extends Document {
|
||||
conversationId: string;
|
||||
title?: string;
|
||||
user?: string;
|
||||
messages?: Types.ObjectId[];
|
||||
agentOptions?: unknown;
|
||||
// Fields provided by conversationPreset (adjust types as needed)
|
||||
endpoint?: string;
|
||||
endpointType?: string;
|
||||
model?: string;
|
||||
region?: string;
|
||||
chatGptLabel?: string;
|
||||
examples?: unknown[];
|
||||
modelLabel?: string;
|
||||
promptPrefix?: string;
|
||||
temperature?: number;
|
||||
top_p?: number;
|
||||
topP?: number;
|
||||
topK?: number;
|
||||
maxOutputTokens?: number;
|
||||
maxTokens?: number;
|
||||
presence_penalty?: number;
|
||||
frequency_penalty?: number;
|
||||
file_ids?: string[];
|
||||
resendImages?: boolean;
|
||||
promptCache?: boolean;
|
||||
thinking?: boolean;
|
||||
thinkingBudget?: number;
|
||||
system?: string;
|
||||
resendFiles?: boolean;
|
||||
imageDetail?: string;
|
||||
agent_id?: string;
|
||||
assistant_id?: string;
|
||||
instructions?: string;
|
||||
stop?: string[];
|
||||
isArchived?: boolean;
|
||||
iconURL?: string;
|
||||
greeting?: string;
|
||||
spec?: string;
|
||||
tags?: string[];
|
||||
tools?: string[];
|
||||
maxContextTokens?: number;
|
||||
max_tokens?: number;
|
||||
reasoning_effort?: string;
|
||||
// Additional fields
|
||||
files?: string[];
|
||||
expiredAt?: Date;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const convoSchema: Schema<IConversation> = new Schema(
|
||||
{
|
||||
conversationId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
required: true,
|
||||
index: true,
|
||||
meiliIndex: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'New Chat',
|
||||
meiliIndex: true,
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
index: true,
|
||||
},
|
||||
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
|
||||
agentOptions: {
|
||||
type: mongoose.Schema.Types.Mixed,
|
||||
},
|
||||
...conversationPreset,
|
||||
agent_id: {
|
||||
type: String,
|
||||
},
|
||||
tags: {
|
||||
type: [String],
|
||||
default: [],
|
||||
meiliIndex: true,
|
||||
},
|
||||
files: {
|
||||
type: [String],
|
||||
},
|
||||
expiredAt: {
|
||||
type: Date,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
convoSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
|
||||
convoSchema.index({ createdAt: 1, updatedAt: 1 });
|
||||
convoSchema.index({ conversationId: 1, user: 1 }, { unique: true });
|
||||
|
||||
export default convoSchema;
|
||||
138
packages/data-schemas/src/schema/defaults.ts
Normal file
138
packages/data-schemas/src/schema/defaults.ts
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import { Schema } from 'mongoose';
|
||||
|
||||
// @ts-ignore
|
||||
export const conversationPreset = {
|
||||
// endpoint: [azureOpenAI, openAI, anthropic, chatGPTBrowser]
|
||||
endpoint: {
|
||||
type: String,
|
||||
default: null,
|
||||
required: true,
|
||||
},
|
||||
endpointType: {
|
||||
type: String,
|
||||
},
|
||||
// for azureOpenAI, openAI, chatGPTBrowser only
|
||||
model: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
// for bedrock only
|
||||
region: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
// for azureOpenAI, openAI only
|
||||
chatGptLabel: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
// for google only
|
||||
examples: { type: [{ type: Schema.Types.Mixed }], default: undefined },
|
||||
modelLabel: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
promptPrefix: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
temperature: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
top_p: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
// for google only
|
||||
topP: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
topK: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
maxOutputTokens: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
maxTokens: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
presence_penalty: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
frequency_penalty: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
file_ids: { type: [{ type: String }], default: undefined },
|
||||
// deprecated
|
||||
resendImages: {
|
||||
type: Boolean,
|
||||
},
|
||||
/* Anthropic only */
|
||||
promptCache: {
|
||||
type: Boolean,
|
||||
},
|
||||
thinking: {
|
||||
type: Boolean,
|
||||
},
|
||||
thinkingBudget: {
|
||||
type: Number,
|
||||
},
|
||||
system: {
|
||||
type: String,
|
||||
},
|
||||
// files
|
||||
resendFiles: {
|
||||
type: Boolean,
|
||||
},
|
||||
imageDetail: {
|
||||
type: String,
|
||||
},
|
||||
/* agents */
|
||||
agent_id: {
|
||||
type: String,
|
||||
},
|
||||
/* assistants */
|
||||
assistant_id: {
|
||||
type: String,
|
||||
},
|
||||
instructions: {
|
||||
type: String,
|
||||
},
|
||||
stop: { type: [{ type: String }], default: undefined },
|
||||
isArchived: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/* UI Components */
|
||||
iconURL: {
|
||||
type: String,
|
||||
},
|
||||
greeting: {
|
||||
type: String,
|
||||
},
|
||||
spec: {
|
||||
type: String,
|
||||
},
|
||||
tags: {
|
||||
type: [String],
|
||||
default: [],
|
||||
},
|
||||
tools: { type: [{ type: String }], default: undefined },
|
||||
maxContextTokens: {
|
||||
type: Number,
|
||||
},
|
||||
max_tokens: {
|
||||
type: Number,
|
||||
},
|
||||
/** omni models only */
|
||||
reasoning_effort: {
|
||||
type: String,
|
||||
},
|
||||
};
|
||||
111
packages/data-schemas/src/schema/file.ts
Normal file
111
packages/data-schemas/src/schema/file.ts
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
||||
import { FileSources } from 'librechat-data-provider';
|
||||
|
||||
// @ts-ignore
|
||||
export interface IMongoFile extends Document {
|
||||
user: Types.ObjectId;
|
||||
conversationId?: string;
|
||||
file_id: string;
|
||||
temp_file_id?: string;
|
||||
bytes: number;
|
||||
text?: string;
|
||||
filename: string;
|
||||
filepath: string;
|
||||
object: 'file';
|
||||
embedded?: boolean;
|
||||
type: string;
|
||||
context?: string;
|
||||
usage: number;
|
||||
source: string;
|
||||
model?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
metadata?: {
|
||||
fileIdentifier?: string;
|
||||
};
|
||||
expiresAt?: Date;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const file: Schema<IMongoFile> = new Schema(
|
||||
{
|
||||
user: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index: true,
|
||||
required: true,
|
||||
},
|
||||
conversationId: {
|
||||
type: String,
|
||||
ref: 'Conversation',
|
||||
index: true,
|
||||
},
|
||||
file_id: {
|
||||
type: String,
|
||||
index: true,
|
||||
required: true,
|
||||
},
|
||||
temp_file_id: {
|
||||
type: String,
|
||||
},
|
||||
bytes: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
filename: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
filepath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
object: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'file',
|
||||
},
|
||||
embedded: {
|
||||
type: Boolean,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
},
|
||||
context: {
|
||||
type: String,
|
||||
},
|
||||
usage: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0,
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
default: FileSources.local,
|
||||
},
|
||||
model: {
|
||||
type: String,
|
||||
},
|
||||
width: Number,
|
||||
height: Number,
|
||||
metadata: {
|
||||
fileIdentifier: String,
|
||||
},
|
||||
expiresAt: {
|
||||
type: Date,
|
||||
expires: 3600, // 1 hour in seconds
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
file.index({ createdAt: 1, updatedAt: 1 });
|
||||
|
||||
export default file;
|
||||
31
packages/data-schemas/src/schema/key.ts
Normal file
31
packages/data-schemas/src/schema/key.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface IKey extends Document {
|
||||
userId: Types.ObjectId;
|
||||
name: string;
|
||||
value: string;
|
||||
expiresAt?: Date;
|
||||
}
|
||||
|
||||
const keySchema: Schema<IKey> = new Schema({
|
||||
userId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
expiresAt: {
|
||||
type: Date,
|
||||
},
|
||||
});
|
||||
|
||||
keySchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
||||
|
||||
export default keySchema;
|
||||
185
packages/data-schemas/src/schema/message.ts
Normal file
185
packages/data-schemas/src/schema/message.ts
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import mongoose, { Schema, Document } from 'mongoose';
|
||||
|
||||
// @ts-ignore
|
||||
export interface IMessage extends Document {
|
||||
messageId: string;
|
||||
conversationId: string;
|
||||
user: string;
|
||||
model?: string;
|
||||
endpoint?: string;
|
||||
conversationSignature?: string;
|
||||
clientId?: string;
|
||||
invocationId?: number;
|
||||
parentMessageId?: string;
|
||||
tokenCount?: number;
|
||||
summaryTokenCount?: number;
|
||||
sender?: string;
|
||||
text?: string;
|
||||
summary?: string;
|
||||
isCreatedByUser: boolean;
|
||||
unfinished?: boolean;
|
||||
error?: boolean;
|
||||
finish_reason?: string;
|
||||
_meiliIndex?: boolean;
|
||||
files?: unknown[];
|
||||
plugin?: {
|
||||
latest?: string;
|
||||
inputs?: unknown[];
|
||||
outputs?: string;
|
||||
};
|
||||
plugins?: unknown[];
|
||||
content?: unknown[];
|
||||
thread_id?: string;
|
||||
iconURL?: string;
|
||||
attachments?: unknown[];
|
||||
expiredAt?: Date;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const messageSchema: Schema<IMessage> = new Schema(
|
||||
{
|
||||
messageId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
required: true,
|
||||
index: true,
|
||||
meiliIndex: true,
|
||||
},
|
||||
conversationId: {
|
||||
type: String,
|
||||
index: true,
|
||||
required: true,
|
||||
meiliIndex: true,
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
index: true,
|
||||
required: true,
|
||||
default: null,
|
||||
},
|
||||
model: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
endpoint: {
|
||||
type: String,
|
||||
},
|
||||
conversationSignature: {
|
||||
type: String,
|
||||
},
|
||||
clientId: {
|
||||
type: String,
|
||||
},
|
||||
invocationId: {
|
||||
type: Number,
|
||||
},
|
||||
parentMessageId: {
|
||||
type: String,
|
||||
},
|
||||
tokenCount: {
|
||||
type: Number,
|
||||
},
|
||||
summaryTokenCount: {
|
||||
type: Number,
|
||||
},
|
||||
sender: {
|
||||
type: String,
|
||||
meiliIndex: true,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
meiliIndex: true,
|
||||
},
|
||||
summary: {
|
||||
type: String,
|
||||
},
|
||||
isCreatedByUser: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
unfinished: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
error: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
finish_reason: {
|
||||
type: String,
|
||||
},
|
||||
_meiliIndex: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
select: false,
|
||||
default: false,
|
||||
},
|
||||
files: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
|
||||
plugin: {
|
||||
type: {
|
||||
latest: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
inputs: {
|
||||
type: [mongoose.Schema.Types.Mixed],
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
outputs: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
default: undefined,
|
||||
},
|
||||
plugins: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
|
||||
content: {
|
||||
type: [{ type: mongoose.Schema.Types.Mixed }],
|
||||
default: undefined,
|
||||
meiliIndex: true,
|
||||
},
|
||||
thread_id: {
|
||||
type: String,
|
||||
},
|
||||
/* frontend components */
|
||||
iconURL: {
|
||||
type: String,
|
||||
},
|
||||
attachments: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
|
||||
/*
|
||||
attachments: {
|
||||
type: [
|
||||
{
|
||||
file_id: String,
|
||||
filename: String,
|
||||
filepath: String,
|
||||
expiresAt: Date,
|
||||
width: Number,
|
||||
height: Number,
|
||||
type: String,
|
||||
conversationId: String,
|
||||
messageId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
toolCallId: String,
|
||||
},
|
||||
],
|
||||
default: undefined,
|
||||
},
|
||||
*/
|
||||
expiredAt: {
|
||||
type: Date,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
messageSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
|
||||
messageSchema.index({ createdAt: 1 });
|
||||
messageSchema.index({ messageId: 1, user: 1 }, { unique: true });
|
||||
|
||||
export default messageSchema;
|
||||
33
packages/data-schemas/src/schema/pluginAuth.ts
Normal file
33
packages/data-schemas/src/schema/pluginAuth.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { Schema, Document } from 'mongoose';
|
||||
|
||||
export interface IPluginAuth extends Document {
|
||||
authField: string;
|
||||
value: string;
|
||||
userId: string;
|
||||
pluginKey?: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const pluginAuthSchema: Schema<IPluginAuth> = new Schema(
|
||||
{
|
||||
authField: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
userId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
pluginKey: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
export default pluginAuthSchema;
|
||||
85
packages/data-schemas/src/schema/preset.ts
Normal file
85
packages/data-schemas/src/schema/preset.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import mongoose, { Schema, Document } from 'mongoose';
|
||||
import { conversationPreset } from './defaults';
|
||||
|
||||
// @ts-ignore
|
||||
export interface IPreset extends Document {
|
||||
presetId: string;
|
||||
title: string;
|
||||
user: string | null;
|
||||
defaultPreset?: boolean;
|
||||
order?: number;
|
||||
// Additional fields from conversationPreset and others will be available via an index signature.
|
||||
endpoint?: string;
|
||||
endpointType?: string;
|
||||
model?: string;
|
||||
region?: string;
|
||||
chatGptLabel?: string;
|
||||
examples?: unknown[];
|
||||
modelLabel?: string;
|
||||
promptPrefix?: string;
|
||||
temperature?: number;
|
||||
top_p?: number;
|
||||
topP?: number;
|
||||
topK?: number;
|
||||
maxOutputTokens?: number;
|
||||
maxTokens?: number;
|
||||
presence_penalty?: number;
|
||||
frequency_penalty?: number;
|
||||
file_ids?: string[];
|
||||
resendImages?: boolean;
|
||||
promptCache?: boolean;
|
||||
thinking?: boolean;
|
||||
thinkingBudget?: number;
|
||||
system?: string;
|
||||
resendFiles?: boolean;
|
||||
imageDetail?: string;
|
||||
agent_id?: string;
|
||||
assistant_id?: string;
|
||||
instructions?: string;
|
||||
stop?: string[];
|
||||
isArchived?: boolean;
|
||||
iconURL?: string;
|
||||
greeting?: string;
|
||||
spec?: string;
|
||||
tags?: string[];
|
||||
tools?: string[];
|
||||
maxContextTokens?: number;
|
||||
max_tokens?: number;
|
||||
reasoning_effort?: string;
|
||||
// end of additional fields
|
||||
agentOptions?: unknown;
|
||||
}
|
||||
|
||||
const presetSchema: Schema<IPreset> = new Schema(
|
||||
{
|
||||
presetId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'New Chat',
|
||||
meiliIndex: true,
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
defaultPreset: {
|
||||
type: Boolean,
|
||||
},
|
||||
order: {
|
||||
type: Number,
|
||||
},
|
||||
...conversationPreset,
|
||||
agentOptions: {
|
||||
type: mongoose.Schema.Types.Mixed,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
export default presetSchema;
|
||||
34
packages/data-schemas/src/schema/project.ts
Normal file
34
packages/data-schemas/src/schema/project.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface IMongoProject extends Document {
|
||||
name: string;
|
||||
promptGroupIds: Types.ObjectId[];
|
||||
agentIds: string[];
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const projectSchema = new Schema<IMongoProject>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
promptGroupIds: {
|
||||
type: [Schema.Types.ObjectId],
|
||||
ref: 'PromptGroup',
|
||||
default: [],
|
||||
},
|
||||
agentIds: {
|
||||
type: [String],
|
||||
ref: 'Agent',
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
export default projectSchema;
|
||||
42
packages/data-schemas/src/schema/prompt.ts
Normal file
42
packages/data-schemas/src/schema/prompt.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface IPrompt extends Document {
|
||||
groupId: Types.ObjectId;
|
||||
author: Types.ObjectId;
|
||||
prompt: string;
|
||||
type: 'text' | 'chat';
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const promptSchema: Schema<IPrompt> = new Schema(
|
||||
{
|
||||
groupId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'PromptGroup',
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
author: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
},
|
||||
prompt: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['text', 'chat'],
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
promptSchema.index({ createdAt: 1, updatedAt: 1 });
|
||||
|
||||
export default promptSchema;
|
||||
85
packages/data-schemas/src/schema/promptGroup.ts
Normal file
85
packages/data-schemas/src/schema/promptGroup.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { Schema, Document, Types } from 'mongoose';
|
||||
import { Constants } from 'librechat-data-provider';
|
||||
|
||||
export interface IPromptGroup {
|
||||
name: string;
|
||||
numberOfGenerations: number;
|
||||
oneliner: string;
|
||||
category: string;
|
||||
projectIds: Types.ObjectId[];
|
||||
productionId: Types.ObjectId;
|
||||
author: Types.ObjectId;
|
||||
authorName: string;
|
||||
command?: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface IPromptGroupDocument extends IPromptGroup, Document {}
|
||||
|
||||
const promptGroupSchema = new Schema<IPromptGroupDocument>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
numberOfGenerations: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
oneliner: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
category: {
|
||||
type: String,
|
||||
default: '',
|
||||
index: true,
|
||||
},
|
||||
projectIds: {
|
||||
type: [Schema.Types.ObjectId],
|
||||
ref: 'Project',
|
||||
index: true,
|
||||
default: [],
|
||||
},
|
||||
productionId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Prompt',
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
author: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
index: true,
|
||||
},
|
||||
authorName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
command: {
|
||||
type: String,
|
||||
index: true,
|
||||
validate: {
|
||||
validator: function (v: unknown): boolean {
|
||||
return v === undefined || v === null || v === '' || /^[a-z0-9-]+$/.test(v);
|
||||
},
|
||||
message: (props: unknown) =>
|
||||
`${props.value} is not a valid command. Only lowercase alphanumeric characters and hyphens are allowed.`,
|
||||
},
|
||||
maxlength: [
|
||||
Constants.COMMANDS_MAX_LENGTH as number,
|
||||
`Command cannot be longer than ${Constants.COMMANDS_MAX_LENGTH} characters`,
|
||||
],
|
||||
}, // Casting here bypasses the type error for the command field.
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
promptGroupSchema.index({ createdAt: 1, updatedAt: 1 });
|
||||
|
||||
export default promptGroupSchema;
|
||||
91
packages/data-schemas/src/schema/role.ts
Normal file
91
packages/data-schemas/src/schema/role.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { Schema, Document } from 'mongoose';
|
||||
import { PermissionTypes, Permissions } from 'librechat-data-provider';
|
||||
|
||||
export interface IRole extends Document {
|
||||
name: string;
|
||||
[PermissionTypes.BOOKMARKS]?: {
|
||||
[Permissions.USE]?: boolean;
|
||||
};
|
||||
[PermissionTypes.PROMPTS]?: {
|
||||
[Permissions.SHARED_GLOBAL]?: boolean;
|
||||
[Permissions.USE]?: boolean;
|
||||
[Permissions.CREATE]?: boolean;
|
||||
};
|
||||
[PermissionTypes.AGENTS]?: {
|
||||
[Permissions.SHARED_GLOBAL]?: boolean;
|
||||
[Permissions.USE]?: boolean;
|
||||
[Permissions.CREATE]?: boolean;
|
||||
};
|
||||
[PermissionTypes.MULTI_CONVO]?: {
|
||||
[Permissions.USE]?: boolean;
|
||||
};
|
||||
[PermissionTypes.TEMPORARY_CHAT]?: {
|
||||
[Permissions.USE]?: boolean;
|
||||
};
|
||||
[PermissionTypes.RUN_CODE]?: {
|
||||
[Permissions.USE]?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const roleSchema: Schema<IRole> = new Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
index: true,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: {
|
||||
[Permissions.USE]: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
[Permissions.USE]: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
[Permissions.CREATE]: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
[Permissions.USE]: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
[Permissions.CREATE]: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
[PermissionTypes.MULTI_CONVO]: {
|
||||
[Permissions.USE]: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: {
|
||||
[Permissions.USE]: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
[PermissionTypes.RUN_CODE]: {
|
||||
[Permissions.USE]: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default roleSchema;
|
||||
26
packages/data-schemas/src/schema/session.ts
Normal file
26
packages/data-schemas/src/schema/session.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface ISession extends Document {
|
||||
refreshTokenHash: string;
|
||||
expiration: Date;
|
||||
user: Types.ObjectId;
|
||||
}
|
||||
|
||||
const sessionSchema: Schema<ISession> = new Schema({
|
||||
refreshTokenHash: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
expiration: {
|
||||
type: Date,
|
||||
required: true,
|
||||
expires: 0,
|
||||
},
|
||||
user: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default sessionSchema;
|
||||
41
packages/data-schemas/src/schema/share.ts
Normal file
41
packages/data-schemas/src/schema/share.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface ISharedLink extends Document {
|
||||
conversationId: string;
|
||||
title?: string;
|
||||
user?: string;
|
||||
messages?: Types.ObjectId[];
|
||||
shareId?: string;
|
||||
isPublic: boolean;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const shareSchema: Schema<ISharedLink> = new Schema(
|
||||
{
|
||||
conversationId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
index: true,
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
index: true,
|
||||
},
|
||||
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
|
||||
shareId: {
|
||||
type: String,
|
||||
index: true,
|
||||
},
|
||||
isPublic: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
export default shareSchema;
|
||||
50
packages/data-schemas/src/schema/token.ts
Normal file
50
packages/data-schemas/src/schema/token.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
export interface IToken extends Document {
|
||||
userId: Types.ObjectId;
|
||||
email?: string;
|
||||
type?: string;
|
||||
identifier?: string;
|
||||
token: string;
|
||||
createdAt: Date;
|
||||
expiresAt: Date;
|
||||
metadata?: Map<string, unknown>;
|
||||
}
|
||||
|
||||
const tokenSchema: Schema<IToken> = new Schema({
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
required: true,
|
||||
ref: 'user',
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
},
|
||||
identifier: {
|
||||
type: String,
|
||||
},
|
||||
token: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
required: true,
|
||||
default: Date.now,
|
||||
},
|
||||
expiresAt: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
metadata: {
|
||||
type: Map,
|
||||
of: Schema.Types.Mixed,
|
||||
},
|
||||
});
|
||||
|
||||
tokenSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
||||
|
||||
export default tokenSchema;
|
||||
55
packages/data-schemas/src/schema/toolCall.ts
Normal file
55
packages/data-schemas/src/schema/toolCall.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
||||
import type { TAttachment } from 'librechat-data-provider';
|
||||
|
||||
export interface IToolCallData extends Document {
|
||||
conversationId: string;
|
||||
messageId: string;
|
||||
toolId: string;
|
||||
user: Types.ObjectId;
|
||||
result?: unknown;
|
||||
attachments?: TAttachment[];
|
||||
blockIndex?: number;
|
||||
partIndex?: number;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const toolCallSchema: Schema<IToolCallData> = new Schema(
|
||||
{
|
||||
conversationId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
messageId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
toolId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
user: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
},
|
||||
result: {
|
||||
type: mongoose.Schema.Types.Mixed,
|
||||
},
|
||||
attachments: {
|
||||
type: mongoose.Schema.Types.Mixed,
|
||||
},
|
||||
blockIndex: {
|
||||
type: Number,
|
||||
},
|
||||
partIndex: {
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
toolCallSchema.index({ messageId: 1, user: 1 });
|
||||
toolCallSchema.index({ conversationId: 1, user: 1 });
|
||||
|
||||
export default toolCallSchema;
|
||||
60
packages/data-schemas/src/schema/transaction.ts
Normal file
60
packages/data-schemas/src/schema/transaction.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import mongoose, { Schema, Document, Types } from 'mongoose';
|
||||
|
||||
// @ts-ignore
|
||||
export interface ITransaction extends Document {
|
||||
user: Types.ObjectId;
|
||||
conversationId?: string;
|
||||
tokenType: 'prompt' | 'completion' | 'credits';
|
||||
model?: string;
|
||||
context?: string;
|
||||
valueKey?: string;
|
||||
rate?: number;
|
||||
rawAmount?: number;
|
||||
tokenValue?: number;
|
||||
inputTokens?: number;
|
||||
writeTokens?: number;
|
||||
readTokens?: number;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
const transactionSchema: Schema<ITransaction> = new Schema(
|
||||
{
|
||||
user: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index: true,
|
||||
required: true,
|
||||
},
|
||||
conversationId: {
|
||||
type: String,
|
||||
ref: 'Conversation',
|
||||
index: true,
|
||||
},
|
||||
tokenType: {
|
||||
type: String,
|
||||
enum: ['prompt', 'completion', 'credits'],
|
||||
required: true,
|
||||
},
|
||||
model: {
|
||||
type: String,
|
||||
},
|
||||
context: {
|
||||
type: String,
|
||||
},
|
||||
valueKey: {
|
||||
type: String,
|
||||
},
|
||||
rate: Number,
|
||||
rawAmount: Number,
|
||||
tokenValue: Number,
|
||||
inputTokens: { type: Number },
|
||||
writeTokens: { type: Number },
|
||||
readTokens: { type: Number },
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
},
|
||||
);
|
||||
|
||||
export default transactionSchema;
|
||||
163
packages/data-schemas/src/schema/user.ts
Normal file
163
packages/data-schemas/src/schema/user.ts
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import { Schema, Document } from 'mongoose';
|
||||
import { SystemRoles } from 'librechat-data-provider';
|
||||
|
||||
export interface IUser extends Document {
|
||||
name?: string;
|
||||
username?: string;
|
||||
email: string;
|
||||
emailVerified: boolean;
|
||||
password?: string;
|
||||
avatar?: string;
|
||||
provider: string;
|
||||
role?: string;
|
||||
googleId?: string;
|
||||
facebookId?: string;
|
||||
openidId?: string;
|
||||
ldapId?: string;
|
||||
githubId?: string;
|
||||
discordId?: string;
|
||||
appleId?: string;
|
||||
plugins?: unknown[];
|
||||
twoFactorEnabled?: boolean;
|
||||
totpSecret?: string;
|
||||
backupCodes?: Array<{
|
||||
codeHash: string;
|
||||
used: boolean;
|
||||
usedAt?: Date | null;
|
||||
}>;
|
||||
refreshToken?: Array<{
|
||||
refreshToken: string;
|
||||
}>;
|
||||
expiresAt?: Date;
|
||||
termsAccepted?: boolean;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
// Session sub-schema
|
||||
const SessionSchema = new Schema(
|
||||
{
|
||||
refreshToken: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
// Backup code sub-schema
|
||||
const BackupCodeSchema = new Schema(
|
||||
{
|
||||
codeHash: { type: String, required: true },
|
||||
used: { type: Boolean, default: false },
|
||||
usedAt: { type: Date, default: null },
|
||||
},
|
||||
{ _id: false },
|
||||
);
|
||||
|
||||
const User = new Schema<IUser>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
lowercase: true,
|
||||
default: '',
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: [true, 'can\'t be blank'],
|
||||
lowercase: true,
|
||||
unique: true,
|
||||
match: [/\S+@\S+\.\S+/, 'is invalid'],
|
||||
index: true,
|
||||
},
|
||||
emailVerified: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
trim: true,
|
||||
minlength: 8,
|
||||
maxlength: 128,
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
provider: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'local',
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
default: SystemRoles.USER,
|
||||
},
|
||||
googleId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
sparse: true,
|
||||
},
|
||||
facebookId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
sparse: true,
|
||||
},
|
||||
openidId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
sparse: true,
|
||||
},
|
||||
ldapId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
sparse: true,
|
||||
},
|
||||
githubId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
sparse: true,
|
||||
},
|
||||
discordId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
sparse: true,
|
||||
},
|
||||
appleId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
sparse: true,
|
||||
},
|
||||
plugins: {
|
||||
type: Array,
|
||||
},
|
||||
twoFactorEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
totpSecret: {
|
||||
type: String,
|
||||
},
|
||||
backupCodes: {
|
||||
type: [BackupCodeSchema],
|
||||
},
|
||||
refreshToken: {
|
||||
type: [SessionSchema],
|
||||
},
|
||||
expiresAt: {
|
||||
type: Date,
|
||||
expires: 604800, // 7 days in seconds
|
||||
},
|
||||
termsAccepted: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
{ timestamps: true },
|
||||
);
|
||||
|
||||
export default User;
|
||||
19
packages/data-schemas/tsconfig.json
Normal file
19
packages/data-schemas/tsconfig.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"declarationDir": "dist/types",
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "tests"]
|
||||
}
|
||||
10
packages/data-schemas/tsconfig.spec.json
Normal file
10
packages/data-schemas/tsconfig.spec.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"outDir": "./dist/tests",
|
||||
"baseUrl": "."
|
||||
},
|
||||
"include": ["specs/**/*", "src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "librechat-mcp",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"type": "module",
|
||||
"description": "MCP services for LibreChat",
|
||||
"main": "dist/index.js",
|
||||
|
|
@ -9,15 +9,13 @@
|
|||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.es.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/types/index.d.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && rollup -c --silent --bundleConfigAsCjs",
|
||||
"build:watch": "rollup -c -w",
|
||||
"rollup:api": "npx rollup -c server-rollup.config.js --bundleConfigAsCjs",
|
||||
"build": "npm run clean && rollup -c --configPlugin=@rollup/plugin-typescript",
|
||||
"build:watch": "rollup -c -w --configPlugin=@rollup/plugin-typescript",
|
||||
"test": "jest --coverage --watch",
|
||||
"test:ci": "jest --coverage --ci",
|
||||
"verify": "npm run test:ci",
|
||||
|
|
@ -48,6 +46,7 @@
|
|||
"@rollup/plugin-node-resolve": "^15.1.0",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
|
|
@ -69,9 +68,9 @@
|
|||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.0.3",
|
||||
"@modelcontextprotocol/sdk": "^1.7.0",
|
||||
"diff": "^7.0.0",
|
||||
"eventsource": "^3.0.1",
|
||||
"eventsource": "^3.0.2",
|
||||
"express": "^4.21.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
// rollup.config.js
|
||||
import typescript from 'rollup-plugin-typescript2';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import pkg from './package.json';
|
||||
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
import terser from '@rollup/plugin-terser';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
||||
|
||||
const plugins = [
|
||||
peerDepsExternal(),
|
||||
resolve(),
|
||||
resolve({
|
||||
preferBuiltins: true,
|
||||
}),
|
||||
replace({
|
||||
__IS_DEV__: process.env.NODE_ENV === 'development',
|
||||
preventAssignment: true,
|
||||
}),
|
||||
commonjs({
|
||||
transformMixedEsModules: true,
|
||||
requireReturnsDefault: 'auto',
|
||||
}),
|
||||
commonjs(),
|
||||
typescript({
|
||||
tsconfig: './tsconfig.json',
|
||||
useTsconfigDeclarationDir: true,
|
||||
|
|
@ -20,27 +29,17 @@ const plugins = [
|
|||
terser(),
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: [
|
||||
{
|
||||
file: pkg.main,
|
||||
format: 'cjs',
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
},
|
||||
{
|
||||
file: pkg.module,
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
},
|
||||
],
|
||||
...{
|
||||
external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.devDependencies || {})],
|
||||
preserveSymlinks: true,
|
||||
plugins,
|
||||
},
|
||||
const esmBuild = {
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
file: pkg.module,
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
},
|
||||
];
|
||||
external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.devDependencies || {})],
|
||||
preserveSymlinks: true,
|
||||
plugins,
|
||||
};
|
||||
|
||||
export default esmBuild;
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
import path from 'path';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import alias from '@rollup/plugin-alias';
|
||||
import json from '@rollup/plugin-json';
|
||||
|
||||
const rootPath = path.resolve(__dirname, '../../');
|
||||
const rootServerPath = path.resolve(__dirname, '../../api');
|
||||
const entryPath = path.resolve(rootPath, 'api/server/index.js');
|
||||
|
||||
console.log('entryPath', entryPath);
|
||||
|
||||
// Define custom aliases here
|
||||
const customAliases = {
|
||||
entries: [{ find: '~', replacement: rootServerPath }],
|
||||
};
|
||||
|
||||
export default {
|
||||
input: entryPath,
|
||||
output: {
|
||||
file: 'test_bundle/bundle.js',
|
||||
format: 'cjs',
|
||||
},
|
||||
plugins: [
|
||||
alias(customAliases),
|
||||
resolve({
|
||||
preferBuiltins: true,
|
||||
extensions: ['.js', '.json', '.node'],
|
||||
}),
|
||||
commonjs(),
|
||||
json(),
|
||||
],
|
||||
external: (id) => {
|
||||
// More selective external function
|
||||
if (/node_modules/.test(id)) {
|
||||
return !id.startsWith('langchain/');
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
@ -43,16 +43,22 @@ export class MCPConnection extends EventEmitter {
|
|||
private isInitializing = false;
|
||||
private reconnectAttempts = 0;
|
||||
iconPath?: string;
|
||||
timeout?: number;
|
||||
|
||||
constructor(serverName: string, private readonly options: t.MCPOptions, private logger?: Logger) {
|
||||
constructor(
|
||||
serverName: string,
|
||||
private readonly options: t.MCPOptions,
|
||||
private logger?: Logger,
|
||||
) {
|
||||
super();
|
||||
this.serverName = serverName;
|
||||
this.logger = logger;
|
||||
this.iconPath = options.iconPath;
|
||||
this.timeout = options.timeout;
|
||||
this.client = new Client(
|
||||
{
|
||||
name: 'librechat-mcp-client',
|
||||
version: '1.0.0',
|
||||
version: '1.1.0',
|
||||
},
|
||||
{
|
||||
capabilities: {},
|
||||
|
|
@ -128,7 +134,12 @@ export class MCPConnection extends EventEmitter {
|
|||
}
|
||||
const url = new URL(options.url);
|
||||
this.logger?.info(`[MCP][${this.serverName}] Creating SSE transport: ${url.toString()}`);
|
||||
const transport = new SSEClientTransport(url);
|
||||
const abortController = new AbortController();
|
||||
const transport = new SSEClientTransport(url, {
|
||||
requestInit: {
|
||||
signal: abortController.signal,
|
||||
},
|
||||
});
|
||||
|
||||
transport.onclose = () => {
|
||||
this.logger?.info(`[MCP][${this.serverName}] SSE transport closed`);
|
||||
|
|
@ -169,6 +180,17 @@ export class MCPConnection extends EventEmitter {
|
|||
this.isInitializing = false;
|
||||
this.shouldStopReconnecting = false;
|
||||
this.reconnectAttempts = 0;
|
||||
/**
|
||||
* // FOR DEBUGGING
|
||||
* // this.client.setRequestHandler(PingRequestSchema, async (request, extra) => {
|
||||
* // this.logger?.info(`[MCP][${this.serverName}] PingRequest: ${JSON.stringify(request)}`);
|
||||
* // if (getEventListeners && extra.signal) {
|
||||
* // const listenerCount = getEventListeners(extra.signal, 'abort').length;
|
||||
* // this.logger?.debug(`Signal has ${listenerCount} abort listeners`);
|
||||
* // }
|
||||
* // return {};
|
||||
* // });
|
||||
*/
|
||||
} else if (state === 'error' && !this.isReconnecting && !this.isInitializing) {
|
||||
this.handleReconnection().catch((error) => {
|
||||
this.logger?.error(`[MCP][${this.serverName}] Reconnection handler failed:`, error);
|
||||
|
|
@ -263,7 +285,7 @@ export class MCPConnection extends EventEmitter {
|
|||
this.transport = this.constructTransport(this.options);
|
||||
this.setupTransportDebugHandlers();
|
||||
|
||||
const connectTimeout = 10000;
|
||||
const connectTimeout = this.options.initTimeout ?? 10000;
|
||||
await Promise.race([
|
||||
this.client.connect(this.transport),
|
||||
new Promise((_resolve, reject) =>
|
||||
|
|
@ -298,6 +320,9 @@ export class MCPConnection extends EventEmitter {
|
|||
|
||||
const originalSend = this.transport.send.bind(this.transport);
|
||||
this.transport.send = async (msg) => {
|
||||
if ('result' in msg && !('method' in msg) && Object.keys(msg.result ?? {}).length === 0) {
|
||||
throw new Error('Empty result');
|
||||
}
|
||||
this.logger?.debug(`[MCP][${this.serverName}] Transport sending: ${JSON.stringify(msg)}`);
|
||||
return originalSend(msg);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
import type { JsonSchemaType } from 'librechat-data-provider';
|
||||
import type { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
||||
import type { JsonSchemaType, MCPOptions } from 'librechat-data-provider';
|
||||
import type { Logger } from 'winston';
|
||||
import type * as t from './types/mcp';
|
||||
import { formatToolContent } from './parsers';
|
||||
|
|
@ -31,13 +32,17 @@ export class MCPManager {
|
|||
return MCPManager.instance;
|
||||
}
|
||||
|
||||
public async initializeMCP(mcpServers: t.MCPServers): Promise<void> {
|
||||
public async initializeMCP(
|
||||
mcpServers: t.MCPServers,
|
||||
processMCPEnv?: (obj: MCPOptions) => MCPOptions,
|
||||
): Promise<void> {
|
||||
this.logger.info('[MCP] Initializing servers');
|
||||
|
||||
const entries = Object.entries(mcpServers);
|
||||
const initializedServers = new Set();
|
||||
const connectionResults = await Promise.allSettled(
|
||||
entries.map(async ([serverName, config], i) => {
|
||||
entries.map(async ([serverName, _config], i) => {
|
||||
const config = processMCPEnv ? processMCPEnv(_config) : _config;
|
||||
const connection = new MCPConnection(serverName, config, this.logger);
|
||||
|
||||
connection.on('connectionChange', (state) => {
|
||||
|
|
@ -159,7 +164,7 @@ export class MCPManager {
|
|||
};
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.warn(`[MCP][${serverName}] Not connected, skipping tool fetch`);
|
||||
this.logger.warn(`[MCP][${serverName}] Error fetching tools:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -183,17 +188,24 @@ export class MCPManager {
|
|||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`[MCP][${serverName}] Error fetching tools`, error);
|
||||
this.logger.error(`[MCP][${serverName}] Error fetching tools:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async callTool(
|
||||
serverName: string,
|
||||
toolName: string,
|
||||
provider: t.Provider,
|
||||
toolArguments?: Record<string, unknown>,
|
||||
): Promise<t.FormattedToolResponse> {
|
||||
async callTool({
|
||||
serverName,
|
||||
toolName,
|
||||
provider,
|
||||
toolArguments,
|
||||
options,
|
||||
}: {
|
||||
serverName: string;
|
||||
toolName: string;
|
||||
provider: t.Provider;
|
||||
toolArguments?: Record<string, unknown>;
|
||||
options?: RequestOptions;
|
||||
}): Promise<t.FormattedToolResponse> {
|
||||
const connection = this.connections.get(serverName);
|
||||
if (!connection) {
|
||||
throw new Error(
|
||||
|
|
@ -209,6 +221,10 @@ export class MCPManager {
|
|||
},
|
||||
},
|
||||
CallToolResultSchema,
|
||||
{
|
||||
timeout: connection.timeout,
|
||||
...options,
|
||||
},
|
||||
);
|
||||
return formatToolContent(result, provider);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue