mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-03 09:08:52 +01:00
Merge branch 'dev' into feat/multi-lang-Terms-of-service
This commit is contained in:
commit
126b1fe412
323 changed files with 20207 additions and 4039 deletions
|
|
@ -187,6 +187,8 @@ export const agents = ({ path = '', options }: { path?: string; options?: object
|
|||
return url;
|
||||
};
|
||||
|
||||
export const revertAgentVersion = (agent_id: string) => `${agents({ path: `${agent_id}/revert` })}`;
|
||||
|
||||
export const files = () => '/api/files';
|
||||
|
||||
export const images = () => `${files()}/images`;
|
||||
|
|
|
|||
|
|
@ -119,7 +119,10 @@ 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')
|
||||
(typedData.model.includes('anthropic.claude-3-7-sonnet') ||
|
||||
/anthropic\.claude-(?:[4-9](?:\.\d+)?(?:-\d+)?-(?:sonnet|opus|haiku)|(?:sonnet|opus|haiku)-[4-9])/.test(
|
||||
typedData.model,
|
||||
))
|
||||
) {
|
||||
if (additionalFields.thinking === undefined) {
|
||||
additionalFields.thinking = true;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { fileConfigSchema } from './file-config';
|
|||
import { FileSources } from './types/files';
|
||||
import { MCPServersSchema } from './mcp';
|
||||
|
||||
export const defaultSocialLogins = ['google', 'facebook', 'openid', 'github', 'discord'];
|
||||
export const defaultSocialLogins = ['google', 'facebook', 'openid', 'github', 'discord', 'saml'];
|
||||
|
||||
export const defaultRetrievalModels = [
|
||||
'gpt-4o',
|
||||
|
|
@ -52,6 +52,7 @@ export const excludedKeys = new Set([
|
|||
'model',
|
||||
'files',
|
||||
'spec',
|
||||
'disableParams',
|
||||
]);
|
||||
|
||||
export enum SettingsViews {
|
||||
|
|
@ -166,6 +167,7 @@ export enum AgentCapabilities {
|
|||
end_after_tools = 'end_after_tools',
|
||||
execute_code = 'execute_code',
|
||||
file_search = 'file_search',
|
||||
web_search = 'web_search',
|
||||
artifacts = 'artifacts',
|
||||
actions = 'actions',
|
||||
tools = 'tools',
|
||||
|
|
@ -231,6 +233,17 @@ export const assistantEndpointSchema = baseEndpointSchema.merge(
|
|||
|
||||
export type TAssistantEndpoint = z.infer<typeof assistantEndpointSchema>;
|
||||
|
||||
export const defaultAgentCapabilities = [
|
||||
AgentCapabilities.execute_code,
|
||||
AgentCapabilities.file_search,
|
||||
AgentCapabilities.web_search,
|
||||
AgentCapabilities.artifacts,
|
||||
AgentCapabilities.actions,
|
||||
AgentCapabilities.tools,
|
||||
AgentCapabilities.chain,
|
||||
AgentCapabilities.ocr,
|
||||
];
|
||||
|
||||
export const agentsEndpointSChema = baseEndpointSchema.merge(
|
||||
z.object({
|
||||
/* agents specific */
|
||||
|
|
@ -241,15 +254,7 @@ export const agentsEndpointSChema = baseEndpointSchema.merge(
|
|||
capabilities: z
|
||||
.array(z.nativeEnum(AgentCapabilities))
|
||||
.optional()
|
||||
.default([
|
||||
AgentCapabilities.execute_code,
|
||||
AgentCapabilities.file_search,
|
||||
AgentCapabilities.artifacts,
|
||||
AgentCapabilities.actions,
|
||||
AgentCapabilities.tools,
|
||||
AgentCapabilities.ocr,
|
||||
AgentCapabilities.chain,
|
||||
]),
|
||||
.default(defaultAgentCapabilities),
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
@ -278,6 +283,12 @@ export const endpointSchema = baseEndpointSchema.merge(
|
|||
headers: z.record(z.any()).optional(),
|
||||
addParams: z.record(z.any()).optional(),
|
||||
dropParams: z.array(z.string()).optional(),
|
||||
customParams: z
|
||||
.object({
|
||||
defaultParamsEndpoint: z.string().default('custom'),
|
||||
paramDefinitions: z.array(z.record(z.any())).optional(),
|
||||
})
|
||||
.strict(),
|
||||
customOrder: z.number().optional(),
|
||||
directEndpoint: z.boolean().optional(),
|
||||
titleMessageRole: z.string().optional(),
|
||||
|
|
@ -486,6 +497,7 @@ export const intefaceSchema = z
|
|||
agents: z.boolean().optional(),
|
||||
temporaryChat: z.boolean().optional(),
|
||||
runCode: z.boolean().optional(),
|
||||
webSearch: z.boolean().optional(),
|
||||
})
|
||||
.default({
|
||||
endpointsMenu: true,
|
||||
|
|
@ -499,6 +511,7 @@ export const intefaceSchema = z
|
|||
agents: true,
|
||||
temporaryChat: true,
|
||||
runCode: true,
|
||||
webSearch: true,
|
||||
});
|
||||
|
||||
export type TInterfaceConfig = z.infer<typeof intefaceSchema>;
|
||||
|
|
@ -533,9 +546,12 @@ export type TStartupConfig = {
|
|||
googleLoginEnabled: boolean;
|
||||
openidLoginEnabled: boolean;
|
||||
appleLoginEnabled: boolean;
|
||||
samlLoginEnabled: boolean;
|
||||
openidLabel: string;
|
||||
openidImageUrl: string;
|
||||
openidAutoRedirect: boolean;
|
||||
samlLabel: string;
|
||||
samlImageUrl: string;
|
||||
/** LDAP Auth Configuration */
|
||||
ldap?: {
|
||||
/** LDAP enabled */
|
||||
|
|
@ -559,6 +575,11 @@ export type TStartupConfig = {
|
|||
instanceProjectId: string;
|
||||
bundlerURL?: string;
|
||||
staticBundlerURL?: string;
|
||||
webSearch?: {
|
||||
searchProvider?: SearchProviders;
|
||||
scraperType?: ScraperTypes;
|
||||
rerankerType?: RerankerTypes;
|
||||
};
|
||||
};
|
||||
|
||||
export enum OCRStrategy {
|
||||
|
|
@ -566,10 +587,52 @@ export enum OCRStrategy {
|
|||
CUSTOM_OCR = 'custom_ocr',
|
||||
}
|
||||
|
||||
export enum SearchCategories {
|
||||
PROVIDERS = 'providers',
|
||||
SCRAPERS = 'scrapers',
|
||||
RERANKERS = 'rerankers',
|
||||
}
|
||||
|
||||
export enum SearchProviders {
|
||||
SERPER = 'serper',
|
||||
SEARXNG = 'searxng',
|
||||
}
|
||||
|
||||
export enum ScraperTypes {
|
||||
FIRECRAWL = 'firecrawl',
|
||||
SERPER = 'serper',
|
||||
}
|
||||
|
||||
export enum RerankerTypes {
|
||||
JINA = 'jina',
|
||||
COHERE = 'cohere',
|
||||
}
|
||||
|
||||
export enum SafeSearchTypes {
|
||||
OFF = 0,
|
||||
MODERATE = 1,
|
||||
STRICT = 2,
|
||||
}
|
||||
|
||||
export const webSearchSchema = z.object({
|
||||
serperApiKey: z.string().optional().default('${SERPER_API_KEY}'),
|
||||
firecrawlApiKey: z.string().optional().default('${FIRECRAWL_API_KEY}'),
|
||||
firecrawlApiUrl: z.string().optional().default('${FIRECRAWL_API_URL}'),
|
||||
jinaApiKey: z.string().optional().default('${JINA_API_KEY}'),
|
||||
cohereApiKey: z.string().optional().default('${COHERE_API_KEY}'),
|
||||
searchProvider: z.nativeEnum(SearchProviders).optional(),
|
||||
scraperType: z.nativeEnum(ScraperTypes).optional(),
|
||||
rerankerType: z.nativeEnum(RerankerTypes).optional(),
|
||||
scraperTimeout: z.number().optional(),
|
||||
safeSearch: z.nativeEnum(SafeSearchTypes).default(SafeSearchTypes.MODERATE),
|
||||
});
|
||||
|
||||
export type TWebSearchConfig = z.infer<typeof webSearchSchema>;
|
||||
|
||||
export const ocrSchema = z.object({
|
||||
mistralModel: z.string().optional(),
|
||||
apiKey: z.string().optional().default('OCR_API_KEY'),
|
||||
baseURL: z.string().optional().default('OCR_BASEURL'),
|
||||
apiKey: z.string().optional().default('${OCR_API_KEY}'),
|
||||
baseURL: z.string().optional().default('${OCR_BASEURL}'),
|
||||
strategy: z.nativeEnum(OCRStrategy).default(OCRStrategy.MISTRAL_OCR),
|
||||
});
|
||||
|
||||
|
|
@ -589,6 +652,7 @@ export const configSchema = z.object({
|
|||
version: z.string(),
|
||||
cache: z.boolean().default(true),
|
||||
ocr: ocrSchema.optional(),
|
||||
webSearch: webSearchSchema.optional(),
|
||||
secureImageLinks: z.boolean().optional(),
|
||||
imageOutputType: z.nativeEnum(EImageOutputType).default(EImageOutputType.PNG),
|
||||
includedTools: z.array(z.string()).optional(),
|
||||
|
|
@ -727,6 +791,10 @@ const sharedOpenAIModels = [
|
|||
];
|
||||
|
||||
const sharedAnthropicModels = [
|
||||
'claude-sonnet-4-20250514',
|
||||
'claude-sonnet-4-latest',
|
||||
'claude-opus-4-20250514',
|
||||
'claude-opus-4-latest',
|
||||
'claude-3-7-sonnet-latest',
|
||||
'claude-3-7-sonnet-20250219',
|
||||
'claude-3-5-haiku-20241022',
|
||||
|
|
@ -886,8 +954,7 @@ export const visionModels = [
|
|||
'gemma',
|
||||
'gemini-exp',
|
||||
'gemini-1.5',
|
||||
'gemini-2.0',
|
||||
'gemini-2.5',
|
||||
'gemini-2',
|
||||
'gemini-3',
|
||||
'moondream',
|
||||
'llama3.2-vision',
|
||||
|
|
@ -895,6 +962,10 @@ export const visionModels = [
|
|||
'llama-3-2-11b-vision',
|
||||
'llama-3.2-90b-vision',
|
||||
'llama-3-2-90b-vision',
|
||||
'llama-4',
|
||||
'claude-opus-4',
|
||||
'claude-sonnet-4',
|
||||
'claude-haiku-4',
|
||||
];
|
||||
export enum VisionModes {
|
||||
generative = 'generative',
|
||||
|
|
@ -1040,6 +1111,10 @@ export enum CacheKeys {
|
|||
* Key for s3 check intervals per user
|
||||
*/
|
||||
S3_EXPIRY_INTERVAL = 'S3_EXPIRY_INTERVAL',
|
||||
/**
|
||||
* key for open id exchanged tokens
|
||||
*/
|
||||
OPENID_EXCHANGED_TOKENS = 'OPENID_EXCHANGED_TOKENS',
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1203,6 +1278,10 @@ export enum SettingsTabValues {
|
|||
* Tab for Data Controls
|
||||
*/
|
||||
DATA = 'data',
|
||||
/**
|
||||
* Tab for Balance Settings
|
||||
*/
|
||||
BALANCE = 'balance',
|
||||
/**
|
||||
* Tab for Account Settings
|
||||
*/
|
||||
|
|
@ -1248,7 +1327,7 @@ export enum Constants {
|
|||
/** Key for the app's version. */
|
||||
VERSION = 'v0.7.8',
|
||||
/** Key for the Custom Config's version (librechat.yaml). */
|
||||
CONFIG_VERSION = '1.2.5',
|
||||
CONFIG_VERSION = '1.2.6',
|
||||
/** 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 */
|
||||
|
|
@ -1316,6 +1395,8 @@ export enum LocalStorageKeys {
|
|||
LAST_MCP_ = 'LAST_MCP_',
|
||||
/** Last checked toggle for Code Interpreter API per conversation ID */
|
||||
LAST_CODE_TOGGLE_ = 'LAST_CODE_TOGGLE_',
|
||||
/** Last checked toggle for Web Search per conversation ID */
|
||||
LAST_WEB_SEARCH_TOGGLE_ = 'LAST_WEB_SEARCH_TOGGLE_',
|
||||
}
|
||||
|
||||
export enum ForkOptions {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export function getUser(): Promise<t.TUser> {
|
|||
return request.get(endpoints.user());
|
||||
}
|
||||
|
||||
export function getUserBalance(): Promise<string> {
|
||||
export function getUserBalance(): Promise<t.TBalanceResponse> {
|
||||
return request.get(endpoints.balance());
|
||||
}
|
||||
|
||||
|
|
@ -431,6 +431,14 @@ export const listAgents = (params: a.AgentListParams): Promise<a.AgentListRespon
|
|||
);
|
||||
};
|
||||
|
||||
export const revertAgentVersion = ({
|
||||
agent_id,
|
||||
version_index,
|
||||
}: {
|
||||
agent_id: string;
|
||||
version_index: number;
|
||||
}): Promise<a.Agent> => request.post(endpoints.revertAgentVersion(agent_id), { version_index });
|
||||
|
||||
/* Tools */
|
||||
|
||||
export const getAvailableAgentTools = (): Promise<s.TPlugin[]> => {
|
||||
|
|
|
|||
|
|
@ -222,6 +222,12 @@ export const fileConfigSchema = z.object({
|
|||
endpoints: z.record(endpointFileConfigSchema).optional(),
|
||||
serverFileSizeLimit: z.number().min(0).optional(),
|
||||
avatarSizeLimit: z.number().min(0).optional(),
|
||||
imageGeneration: z
|
||||
.object({
|
||||
percentage: z.number().min(0).max(100).optional(),
|
||||
px: z.number().min(0).optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
/** Helper function to safely convert string patterns to RegExp objects */
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ export function validateSettingDefinitions(settings: SettingsConfiguration): voi
|
|||
// continue;
|
||||
}
|
||||
setting.includeInput =
|
||||
setting.type === SettingTypes.Number ? setting.includeInput ?? true : false; // Default to true if type is number
|
||||
setting.type === SettingTypes.Number ? (setting.includeInput ?? true) : false; // Default to true if type is number
|
||||
}
|
||||
|
||||
if (setting.component === ComponentTypes.Slider && setting.type === SettingTypes.Number) {
|
||||
|
|
@ -445,7 +445,8 @@ export function validateSettingDefinitions(settings: SettingsConfiguration): voi
|
|||
|
||||
// Validate optionType and conversation schema
|
||||
if (setting.optionType !== OptionTypes.Custom) {
|
||||
const conversationSchema = tConversationSchema.shape[setting.key as keyof TConversation];
|
||||
const conversationSchema =
|
||||
tConversationSchema.shape[setting.key as keyof Omit<TConversation, 'disableParams'>];
|
||||
if (!conversationSchema) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
|
|
@ -466,7 +467,7 @@ export function validateSettingDefinitions(settings: SettingsConfiguration): voi
|
|||
}
|
||||
|
||||
/* Default value checks */
|
||||
if (setting.type === SettingTypes.Number && isNaN(setting.default as number)) {
|
||||
if (setting.type === SettingTypes.Number && isNaN(setting.default as number) && setting.default != null) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid default value for setting ${setting.key}. Must be a number.`,
|
||||
|
|
@ -474,7 +475,7 @@ export function validateSettingDefinitions(settings: SettingsConfiguration): voi
|
|||
});
|
||||
}
|
||||
|
||||
if (setting.type === SettingTypes.Boolean && typeof setting.default !== 'boolean') {
|
||||
if (setting.type === SettingTypes.Boolean && typeof setting.default !== 'boolean' && setting.default != null) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid default value for setting ${setting.key}. Must be a boolean.`,
|
||||
|
|
@ -484,7 +485,7 @@ export function validateSettingDefinitions(settings: SettingsConfiguration): voi
|
|||
|
||||
if (
|
||||
(setting.type === SettingTypes.String || setting.type === SettingTypes.Enum) &&
|
||||
typeof setting.default !== 'string'
|
||||
typeof setting.default !== 'string' && setting.default != null
|
||||
) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ export * from './generate';
|
|||
export * from './models';
|
||||
/* mcp */
|
||||
export * from './mcp';
|
||||
/* web search */
|
||||
export * from './web';
|
||||
/* RBAC */
|
||||
export * from './permissions';
|
||||
export * from './roles';
|
||||
|
|
@ -25,6 +27,7 @@ export * from './types/files';
|
|||
export * from './types/mutations';
|
||||
export * from './types/queries';
|
||||
export * from './types/runs';
|
||||
export * from './types/web';
|
||||
/* query/mutation keys */
|
||||
export * from './keys';
|
||||
/* api call helpers */
|
||||
|
|
@ -36,3 +39,4 @@ import * as dataService from './data-service';
|
|||
export * from './utils';
|
||||
export * from './actions';
|
||||
export { default as createPayload } from './createPayload';
|
||||
export * from './parameterSettings';
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export enum MutationKeys {
|
|||
updateAgentAction = 'updateAgentAction',
|
||||
deleteAction = 'deleteAction',
|
||||
deleteAgentAction = 'deleteAgentAction',
|
||||
revertAgentVersion = 'revertAgentVersion',
|
||||
deleteUser = 'deleteUser',
|
||||
updateRole = 'updateRole',
|
||||
enableTwoFactor = 'enableTwoFactor',
|
||||
|
|
|
|||
|
|
@ -53,9 +53,10 @@ export const WebSocketOptionsSchema = BaseOptionsSchema.extend({
|
|||
type: z.literal('websocket').optional(),
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.transform((val: string) => extractEnvVariable(val))
|
||||
.pipe(z.string().url())
|
||||
.refine(
|
||||
(val) => {
|
||||
(val: string) => {
|
||||
const protocol = new URL(val).protocol;
|
||||
return protocol === 'ws:' || protocol === 'wss:';
|
||||
},
|
||||
|
|
@ -70,9 +71,10 @@ export const SSEOptionsSchema = BaseOptionsSchema.extend({
|
|||
headers: z.record(z.string(), z.string()).optional(),
|
||||
url: z
|
||||
.string()
|
||||
.url()
|
||||
.transform((val: string) => extractEnvVariable(val))
|
||||
.pipe(z.string().url())
|
||||
.refine(
|
||||
(val) => {
|
||||
(val: string) => {
|
||||
const protocol = new URL(val).protocol;
|
||||
return protocol !== 'ws:' && protocol !== 'wss:';
|
||||
},
|
||||
|
|
@ -85,15 +87,19 @@ export const SSEOptionsSchema = BaseOptionsSchema.extend({
|
|||
export const StreamableHTTPOptionsSchema = BaseOptionsSchema.extend({
|
||||
type: z.literal('streamable-http'),
|
||||
headers: z.record(z.string(), z.string()).optional(),
|
||||
url: z.string().url().refine(
|
||||
(val) => {
|
||||
url: z
|
||||
.string()
|
||||
.transform((val: string) => extractEnvVariable(val))
|
||||
.pipe(z.string().url())
|
||||
.refine(
|
||||
(val: string) => {
|
||||
const protocol = new URL(val).protocol;
|
||||
return protocol !== 'ws:' && protocol !== 'wss:';
|
||||
},
|
||||
{
|
||||
message: 'Streamable HTTP URL must not start with ws:// or wss://',
|
||||
},
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
export const MCPOptionsSchema = z.union([
|
||||
|
|
@ -138,5 +144,9 @@ export function processMCPEnv(obj: Readonly<MCPOptions>, userId?: string): MCPOp
|
|||
newObj.headers = processedHeaders;
|
||||
}
|
||||
|
||||
if ('url' in newObj && newObj.url) {
|
||||
newObj.url = extractEnvVariable(newObj.url);
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
|
|
|||
726
packages/data-provider/src/parameterSettings.ts
Normal file
726
packages/data-provider/src/parameterSettings.ts
Normal file
|
|
@ -0,0 +1,726 @@
|
|||
import {
|
||||
ImageDetail,
|
||||
EModelEndpoint,
|
||||
openAISettings,
|
||||
googleSettings,
|
||||
ReasoningEffort,
|
||||
BedrockProviders,
|
||||
anthropicSettings,
|
||||
} from './types';
|
||||
import { SettingDefinition, SettingsConfiguration } from './generate';
|
||||
|
||||
// Base definitions
|
||||
const baseDefinitions: Record<string, SettingDefinition> = {
|
||||
model: {
|
||||
key: 'model',
|
||||
label: 'com_ui_model',
|
||||
labelCode: true,
|
||||
type: 'string',
|
||||
component: 'dropdown',
|
||||
optionType: 'model',
|
||||
selectPlaceholder: 'com_ui_select_model',
|
||||
searchPlaceholder: 'com_ui_select_search_model',
|
||||
searchPlaceholderCode: true,
|
||||
selectPlaceholderCode: true,
|
||||
columnSpan: 4,
|
||||
},
|
||||
temperature: {
|
||||
key: 'temperature',
|
||||
label: 'com_endpoint_temperature',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_openai_temp',
|
||||
descriptionCode: true,
|
||||
type: 'number',
|
||||
component: 'slider',
|
||||
optionType: 'model',
|
||||
columnSpan: 4,
|
||||
},
|
||||
topP: {
|
||||
key: 'topP',
|
||||
label: 'com_endpoint_top_p',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_anthropic_topp',
|
||||
descriptionCode: true,
|
||||
type: 'number',
|
||||
component: 'slider',
|
||||
optionType: 'model',
|
||||
columnSpan: 4,
|
||||
},
|
||||
stop: {
|
||||
key: 'stop',
|
||||
label: 'com_endpoint_stop',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_openai_stop',
|
||||
descriptionCode: true,
|
||||
placeholder: 'com_endpoint_stop_placeholder',
|
||||
placeholderCode: true,
|
||||
type: 'array',
|
||||
default: [],
|
||||
component: 'tags',
|
||||
optionType: 'conversation',
|
||||
minTags: 0,
|
||||
maxTags: 4,
|
||||
},
|
||||
imageDetail: {
|
||||
key: 'imageDetail',
|
||||
label: 'com_endpoint_plug_image_detail',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_openai_detail',
|
||||
descriptionCode: true,
|
||||
type: 'enum',
|
||||
default: ImageDetail.auto,
|
||||
component: 'slider',
|
||||
options: [ImageDetail.low, ImageDetail.auto, ImageDetail.high],
|
||||
optionType: 'conversation',
|
||||
columnSpan: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const createDefinition = (
|
||||
base: Partial<SettingDefinition>,
|
||||
overrides: Partial<SettingDefinition>,
|
||||
): SettingDefinition => {
|
||||
return { ...base, ...overrides } as SettingDefinition;
|
||||
};
|
||||
|
||||
const librechat: Record<string, SettingDefinition> = {
|
||||
modelLabel: {
|
||||
key: 'modelLabel',
|
||||
label: 'com_endpoint_custom_name',
|
||||
labelCode: true,
|
||||
type: 'string',
|
||||
default: '',
|
||||
component: 'input',
|
||||
placeholder: 'com_endpoint_openai_custom_name_placeholder',
|
||||
placeholderCode: true,
|
||||
optionType: 'conversation',
|
||||
},
|
||||
maxContextTokens: {
|
||||
key: 'maxContextTokens',
|
||||
label: 'com_endpoint_context_tokens',
|
||||
labelCode: true,
|
||||
type: 'number',
|
||||
component: 'input',
|
||||
placeholder: 'com_nav_theme_system',
|
||||
placeholderCode: true,
|
||||
description: 'com_endpoint_context_info',
|
||||
descriptionCode: true,
|
||||
optionType: 'model',
|
||||
columnSpan: 2,
|
||||
},
|
||||
resendFiles: {
|
||||
key: 'resendFiles',
|
||||
label: 'com_endpoint_plug_resend_files',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_openai_resend_files',
|
||||
descriptionCode: true,
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
component: 'switch',
|
||||
optionType: 'conversation',
|
||||
showDefault: false,
|
||||
columnSpan: 2,
|
||||
},
|
||||
promptPrefix: {
|
||||
key: 'promptPrefix',
|
||||
label: 'com_endpoint_prompt_prefix',
|
||||
labelCode: true,
|
||||
type: 'string',
|
||||
default: '',
|
||||
component: 'textarea',
|
||||
placeholder: 'com_endpoint_openai_prompt_prefix_placeholder',
|
||||
placeholderCode: true,
|
||||
optionType: 'model',
|
||||
},
|
||||
};
|
||||
|
||||
const openAIParams: Record<string, SettingDefinition> = {
|
||||
chatGptLabel: {
|
||||
...librechat.modelLabel,
|
||||
key: 'chatGptLabel',
|
||||
},
|
||||
promptPrefix: librechat.promptPrefix,
|
||||
temperature: createDefinition(baseDefinitions.temperature, {
|
||||
default: openAISettings.temperature.default,
|
||||
range: {
|
||||
min: openAISettings.temperature.min,
|
||||
max: openAISettings.temperature.max,
|
||||
step: openAISettings.temperature.step,
|
||||
},
|
||||
}),
|
||||
top_p: createDefinition(baseDefinitions.topP, {
|
||||
key: 'top_p',
|
||||
default: openAISettings.top_p.default,
|
||||
range: {
|
||||
min: openAISettings.top_p.min,
|
||||
max: openAISettings.top_p.max,
|
||||
step: openAISettings.top_p.step,
|
||||
},
|
||||
}),
|
||||
frequency_penalty: {
|
||||
key: 'frequency_penalty',
|
||||
label: 'com_endpoint_frequency_penalty',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_openai_freq',
|
||||
descriptionCode: true,
|
||||
type: 'number',
|
||||
default: openAISettings.frequency_penalty.default,
|
||||
range: {
|
||||
min: openAISettings.frequency_penalty.min,
|
||||
max: openAISettings.frequency_penalty.max,
|
||||
step: openAISettings.frequency_penalty.step,
|
||||
},
|
||||
component: 'slider',
|
||||
optionType: 'model',
|
||||
columnSpan: 4,
|
||||
},
|
||||
presence_penalty: {
|
||||
key: 'presence_penalty',
|
||||
label: 'com_endpoint_presence_penalty',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_openai_pres',
|
||||
descriptionCode: true,
|
||||
type: 'number',
|
||||
default: openAISettings.presence_penalty.default,
|
||||
range: {
|
||||
min: openAISettings.presence_penalty.min,
|
||||
max: openAISettings.presence_penalty.max,
|
||||
step: openAISettings.presence_penalty.step,
|
||||
},
|
||||
component: 'slider',
|
||||
optionType: 'model',
|
||||
columnSpan: 4,
|
||||
},
|
||||
max_tokens: {
|
||||
key: 'max_tokens',
|
||||
label: 'com_endpoint_max_output_tokens',
|
||||
labelCode: true,
|
||||
type: 'number',
|
||||
component: 'input',
|
||||
description: 'com_endpoint_openai_max_tokens',
|
||||
descriptionCode: true,
|
||||
placeholder: 'com_nav_theme_system',
|
||||
placeholderCode: true,
|
||||
optionType: 'model',
|
||||
columnSpan: 2,
|
||||
},
|
||||
reasoning_effort: {
|
||||
key: 'reasoning_effort',
|
||||
label: 'com_endpoint_reasoning_effort',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_openai_reasoning_effort',
|
||||
descriptionCode: true,
|
||||
type: 'enum',
|
||||
default: ReasoningEffort.medium,
|
||||
component: 'slider',
|
||||
options: [ReasoningEffort.low, ReasoningEffort.medium, ReasoningEffort.high],
|
||||
optionType: 'model',
|
||||
columnSpan: 4,
|
||||
},
|
||||
};
|
||||
|
||||
const anthropic: Record<string, SettingDefinition> = {
|
||||
maxOutputTokens: {
|
||||
key: 'maxOutputTokens',
|
||||
label: 'com_endpoint_max_output_tokens',
|
||||
labelCode: true,
|
||||
type: 'number',
|
||||
component: 'input',
|
||||
description: 'com_endpoint_anthropic_maxoutputtokens',
|
||||
descriptionCode: true,
|
||||
placeholder: 'com_nav_theme_system',
|
||||
placeholderCode: true,
|
||||
range: {
|
||||
min: anthropicSettings.maxOutputTokens.min,
|
||||
max: anthropicSettings.maxOutputTokens.max,
|
||||
step: anthropicSettings.maxOutputTokens.step,
|
||||
},
|
||||
optionType: 'model',
|
||||
columnSpan: 2,
|
||||
},
|
||||
temperature: createDefinition(baseDefinitions.temperature, {
|
||||
default: anthropicSettings.temperature.default,
|
||||
range: {
|
||||
min: anthropicSettings.temperature.min,
|
||||
max: anthropicSettings.temperature.max,
|
||||
step: anthropicSettings.temperature.step,
|
||||
},
|
||||
}),
|
||||
topP: createDefinition(baseDefinitions.topP, {
|
||||
default: anthropicSettings.topP.default,
|
||||
range: {
|
||||
min: anthropicSettings.topP.min,
|
||||
max: anthropicSettings.topP.max,
|
||||
step: anthropicSettings.topP.step,
|
||||
},
|
||||
}),
|
||||
topK: {
|
||||
key: 'topK',
|
||||
label: 'com_endpoint_top_k',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_anthropic_topk',
|
||||
descriptionCode: true,
|
||||
type: 'number',
|
||||
default: anthropicSettings.topK.default,
|
||||
range: {
|
||||
min: anthropicSettings.topK.min,
|
||||
max: anthropicSettings.topK.max,
|
||||
step: anthropicSettings.topK.step,
|
||||
},
|
||||
component: 'slider',
|
||||
optionType: 'model',
|
||||
columnSpan: 4,
|
||||
},
|
||||
promptCache: {
|
||||
key: 'promptCache',
|
||||
label: 'com_endpoint_prompt_cache',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_anthropic_prompt_cache',
|
||||
descriptionCode: true,
|
||||
type: 'boolean',
|
||||
default: anthropicSettings.promptCache.default,
|
||||
component: 'switch',
|
||||
optionType: 'conversation',
|
||||
showDefault: false,
|
||||
columnSpan: 2,
|
||||
},
|
||||
thinking: {
|
||||
key: 'thinking',
|
||||
label: 'com_endpoint_thinking',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_anthropic_thinking',
|
||||
descriptionCode: true,
|
||||
type: 'boolean',
|
||||
default: anthropicSettings.thinking.default,
|
||||
component: 'switch',
|
||||
optionType: 'conversation',
|
||||
showDefault: false,
|
||||
columnSpan: 2,
|
||||
},
|
||||
thinkingBudget: {
|
||||
key: 'thinkingBudget',
|
||||
label: 'com_endpoint_thinking_budget',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_anthropic_thinking_budget',
|
||||
descriptionCode: true,
|
||||
type: 'number',
|
||||
component: 'input',
|
||||
default: anthropicSettings.thinkingBudget.default,
|
||||
range: {
|
||||
min: anthropicSettings.thinkingBudget.min,
|
||||
max: anthropicSettings.thinkingBudget.max,
|
||||
step: anthropicSettings.thinkingBudget.step,
|
||||
},
|
||||
optionType: 'conversation',
|
||||
columnSpan: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const bedrock: Record<string, SettingDefinition> = {
|
||||
system: {
|
||||
key: 'system',
|
||||
label: 'com_endpoint_prompt_prefix',
|
||||
labelCode: true,
|
||||
type: 'string',
|
||||
default: '',
|
||||
component: 'textarea',
|
||||
placeholder: 'com_endpoint_openai_prompt_prefix_placeholder',
|
||||
placeholderCode: true,
|
||||
optionType: 'model',
|
||||
},
|
||||
region: {
|
||||
key: 'region',
|
||||
type: 'string',
|
||||
label: 'com_ui_region',
|
||||
labelCode: true,
|
||||
component: 'combobox',
|
||||
optionType: 'conversation',
|
||||
selectPlaceholder: 'com_ui_select_region',
|
||||
searchPlaceholder: 'com_ui_select_search_region',
|
||||
searchPlaceholderCode: true,
|
||||
selectPlaceholderCode: true,
|
||||
columnSpan: 2,
|
||||
},
|
||||
maxTokens: {
|
||||
key: 'maxTokens',
|
||||
label: 'com_endpoint_max_output_tokens',
|
||||
labelCode: true,
|
||||
type: 'number',
|
||||
component: 'input',
|
||||
placeholder: 'com_endpoint_anthropic_maxoutputtokens',
|
||||
placeholderCode: true,
|
||||
optionType: 'model',
|
||||
columnSpan: 2,
|
||||
},
|
||||
temperature: createDefinition(baseDefinitions.temperature, {
|
||||
default: 1,
|
||||
range: { min: 0, max: 1, step: 0.01 },
|
||||
}),
|
||||
topK: createDefinition(anthropic.topK, {
|
||||
range: { min: 0, max: 500, step: 1 },
|
||||
}),
|
||||
topP: createDefinition(baseDefinitions.topP, {
|
||||
default: 0.999,
|
||||
range: { min: 0, max: 1, step: 0.01 },
|
||||
}),
|
||||
};
|
||||
|
||||
const mistral: Record<string, SettingDefinition> = {
|
||||
temperature: createDefinition(baseDefinitions.temperature, {
|
||||
default: 0.7,
|
||||
range: { min: 0, max: 1, step: 0.01 },
|
||||
}),
|
||||
topP: createDefinition(baseDefinitions.topP, {
|
||||
range: { min: 0, max: 1, step: 0.01 },
|
||||
}),
|
||||
};
|
||||
|
||||
const cohere: Record<string, SettingDefinition> = {
|
||||
temperature: createDefinition(baseDefinitions.temperature, {
|
||||
default: 0.3,
|
||||
range: { min: 0, max: 1, step: 0.01 },
|
||||
}),
|
||||
topP: createDefinition(baseDefinitions.topP, {
|
||||
default: 0.75,
|
||||
range: { min: 0.01, max: 0.99, step: 0.01 },
|
||||
}),
|
||||
};
|
||||
|
||||
const meta: Record<string, SettingDefinition> = {
|
||||
temperature: createDefinition(baseDefinitions.temperature, {
|
||||
default: 0.5,
|
||||
range: { min: 0, max: 1, step: 0.01 },
|
||||
}),
|
||||
topP: createDefinition(baseDefinitions.topP, {
|
||||
default: 0.9,
|
||||
range: { min: 0, max: 1, step: 0.01 },
|
||||
}),
|
||||
};
|
||||
|
||||
const google: Record<string, SettingDefinition> = {
|
||||
temperature: createDefinition(baseDefinitions.temperature, {
|
||||
default: googleSettings.temperature.default,
|
||||
range: {
|
||||
min: googleSettings.temperature.min,
|
||||
max: googleSettings.temperature.max,
|
||||
step: googleSettings.temperature.step,
|
||||
},
|
||||
}),
|
||||
topP: createDefinition(baseDefinitions.topP, {
|
||||
default: googleSettings.topP.default,
|
||||
range: {
|
||||
min: googleSettings.topP.min,
|
||||
max: googleSettings.topP.max,
|
||||
step: googleSettings.topP.step,
|
||||
},
|
||||
}),
|
||||
topK: {
|
||||
key: 'topK',
|
||||
label: 'com_endpoint_top_k',
|
||||
labelCode: true,
|
||||
description: 'com_endpoint_google_topk',
|
||||
descriptionCode: true,
|
||||
type: 'number',
|
||||
default: googleSettings.topK.default,
|
||||
range: {
|
||||
min: googleSettings.topK.min,
|
||||
max: googleSettings.topK.max,
|
||||
step: googleSettings.topK.step,
|
||||
},
|
||||
component: 'slider',
|
||||
optionType: 'model',
|
||||
columnSpan: 4,
|
||||
},
|
||||
maxOutputTokens: {
|
||||
key: 'maxOutputTokens',
|
||||
label: 'com_endpoint_max_output_tokens',
|
||||
labelCode: true,
|
||||
type: 'number',
|
||||
component: 'input',
|
||||
description: 'com_endpoint_google_maxoutputtokens',
|
||||
descriptionCode: true,
|
||||
placeholder: 'com_nav_theme_system',
|
||||
placeholderCode: true,
|
||||
default: googleSettings.maxOutputTokens.default,
|
||||
range: {
|
||||
min: googleSettings.maxOutputTokens.min,
|
||||
max: googleSettings.maxOutputTokens.max,
|
||||
step: googleSettings.maxOutputTokens.step,
|
||||
},
|
||||
optionType: 'model',
|
||||
columnSpan: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const googleConfig: SettingsConfiguration = [
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
librechat.maxContextTokens,
|
||||
google.maxOutputTokens,
|
||||
google.temperature,
|
||||
google.topP,
|
||||
google.topK,
|
||||
librechat.resendFiles,
|
||||
];
|
||||
|
||||
const googleCol1: SettingsConfiguration = [
|
||||
baseDefinitions.model as SettingDefinition,
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
];
|
||||
|
||||
const googleCol2: SettingsConfiguration = [
|
||||
librechat.maxContextTokens,
|
||||
google.maxOutputTokens,
|
||||
google.temperature,
|
||||
google.topP,
|
||||
google.topK,
|
||||
librechat.resendFiles,
|
||||
];
|
||||
|
||||
const openAI: SettingsConfiguration = [
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
librechat.maxContextTokens,
|
||||
openAIParams.max_tokens,
|
||||
openAIParams.temperature,
|
||||
openAIParams.top_p,
|
||||
openAIParams.frequency_penalty,
|
||||
openAIParams.presence_penalty,
|
||||
baseDefinitions.stop,
|
||||
librechat.resendFiles,
|
||||
baseDefinitions.imageDetail,
|
||||
openAIParams.reasoning_effort,
|
||||
];
|
||||
|
||||
const openAICol1: SettingsConfiguration = [
|
||||
baseDefinitions.model as SettingDefinition,
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
];
|
||||
|
||||
const openAICol2: SettingsConfiguration = [
|
||||
librechat.maxContextTokens,
|
||||
openAIParams.max_tokens,
|
||||
openAIParams.temperature,
|
||||
openAIParams.top_p,
|
||||
openAIParams.frequency_penalty,
|
||||
openAIParams.presence_penalty,
|
||||
baseDefinitions.stop,
|
||||
openAIParams.reasoning_effort,
|
||||
librechat.resendFiles,
|
||||
baseDefinitions.imageDetail,
|
||||
];
|
||||
|
||||
const anthropicConfig: SettingsConfiguration = [
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
librechat.maxContextTokens,
|
||||
anthropic.maxOutputTokens,
|
||||
anthropic.temperature,
|
||||
anthropic.topP,
|
||||
anthropic.topK,
|
||||
librechat.resendFiles,
|
||||
anthropic.promptCache,
|
||||
anthropic.thinking,
|
||||
anthropic.thinkingBudget,
|
||||
];
|
||||
|
||||
const anthropicCol1: SettingsConfiguration = [
|
||||
baseDefinitions.model as SettingDefinition,
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
];
|
||||
|
||||
const anthropicCol2: SettingsConfiguration = [
|
||||
librechat.maxContextTokens,
|
||||
anthropic.maxOutputTokens,
|
||||
anthropic.temperature,
|
||||
anthropic.topP,
|
||||
anthropic.topK,
|
||||
librechat.resendFiles,
|
||||
anthropic.promptCache,
|
||||
anthropic.thinking,
|
||||
anthropic.thinkingBudget,
|
||||
];
|
||||
|
||||
const bedrockAnthropic: SettingsConfiguration = [
|
||||
librechat.modelLabel,
|
||||
bedrock.system,
|
||||
librechat.maxContextTokens,
|
||||
bedrock.maxTokens,
|
||||
bedrock.temperature,
|
||||
bedrock.topP,
|
||||
bedrock.topK,
|
||||
baseDefinitions.stop,
|
||||
librechat.resendFiles,
|
||||
bedrock.region,
|
||||
anthropic.thinking,
|
||||
anthropic.thinkingBudget,
|
||||
];
|
||||
|
||||
const bedrockMistral: SettingsConfiguration = [
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
librechat.maxContextTokens,
|
||||
bedrock.maxTokens,
|
||||
mistral.temperature,
|
||||
mistral.topP,
|
||||
librechat.resendFiles,
|
||||
bedrock.region,
|
||||
];
|
||||
|
||||
const bedrockCohere: SettingsConfiguration = [
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
librechat.maxContextTokens,
|
||||
bedrock.maxTokens,
|
||||
cohere.temperature,
|
||||
cohere.topP,
|
||||
librechat.resendFiles,
|
||||
bedrock.region,
|
||||
];
|
||||
|
||||
const bedrockGeneral: SettingsConfiguration = [
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
librechat.maxContextTokens,
|
||||
meta.temperature,
|
||||
meta.topP,
|
||||
librechat.resendFiles,
|
||||
bedrock.region,
|
||||
];
|
||||
|
||||
const bedrockAnthropicCol1: SettingsConfiguration = [
|
||||
baseDefinitions.model as SettingDefinition,
|
||||
librechat.modelLabel,
|
||||
bedrock.system,
|
||||
baseDefinitions.stop,
|
||||
];
|
||||
|
||||
const bedrockAnthropicCol2: SettingsConfiguration = [
|
||||
librechat.maxContextTokens,
|
||||
bedrock.maxTokens,
|
||||
bedrock.temperature,
|
||||
bedrock.topP,
|
||||
bedrock.topK,
|
||||
librechat.resendFiles,
|
||||
bedrock.region,
|
||||
anthropic.thinking,
|
||||
anthropic.thinkingBudget,
|
||||
];
|
||||
|
||||
const bedrockMistralCol1: SettingsConfiguration = [
|
||||
baseDefinitions.model as SettingDefinition,
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
];
|
||||
|
||||
const bedrockMistralCol2: SettingsConfiguration = [
|
||||
librechat.maxContextTokens,
|
||||
bedrock.maxTokens,
|
||||
mistral.temperature,
|
||||
mistral.topP,
|
||||
librechat.resendFiles,
|
||||
bedrock.region,
|
||||
];
|
||||
|
||||
const bedrockCohereCol1: SettingsConfiguration = [
|
||||
baseDefinitions.model as SettingDefinition,
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
];
|
||||
|
||||
const bedrockCohereCol2: SettingsConfiguration = [
|
||||
librechat.maxContextTokens,
|
||||
bedrock.maxTokens,
|
||||
cohere.temperature,
|
||||
cohere.topP,
|
||||
librechat.resendFiles,
|
||||
bedrock.region,
|
||||
];
|
||||
|
||||
const bedrockGeneralCol1: SettingsConfiguration = [
|
||||
baseDefinitions.model as SettingDefinition,
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
];
|
||||
|
||||
const bedrockGeneralCol2: SettingsConfiguration = [
|
||||
librechat.maxContextTokens,
|
||||
meta.temperature,
|
||||
meta.topP,
|
||||
librechat.resendFiles,
|
||||
bedrock.region,
|
||||
];
|
||||
|
||||
export const paramSettings: Record<string, SettingsConfiguration | undefined> = {
|
||||
[EModelEndpoint.openAI]: openAI,
|
||||
[EModelEndpoint.azureOpenAI]: openAI,
|
||||
[EModelEndpoint.custom]: openAI,
|
||||
[EModelEndpoint.anthropic]: anthropicConfig,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Anthropic}`]: bedrockAnthropic,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.MistralAI}`]: bedrockMistral,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Cohere}`]: bedrockCohere,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Meta}`]: bedrockGeneral,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.AI21}`]: bedrockGeneral,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Amazon}`]: bedrockGeneral,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.DeepSeek}`]: bedrockGeneral,
|
||||
[EModelEndpoint.google]: googleConfig,
|
||||
};
|
||||
|
||||
const openAIColumns = {
|
||||
col1: openAICol1,
|
||||
col2: openAICol2,
|
||||
};
|
||||
|
||||
const bedrockGeneralColumns = {
|
||||
col1: bedrockGeneralCol1,
|
||||
col2: bedrockGeneralCol2,
|
||||
};
|
||||
|
||||
export const presetSettings: Record<
|
||||
string,
|
||||
| {
|
||||
col1: SettingsConfiguration;
|
||||
col2: SettingsConfiguration;
|
||||
}
|
||||
| undefined
|
||||
> = {
|
||||
[EModelEndpoint.openAI]: openAIColumns,
|
||||
[EModelEndpoint.azureOpenAI]: openAIColumns,
|
||||
[EModelEndpoint.custom]: openAIColumns,
|
||||
[EModelEndpoint.anthropic]: {
|
||||
col1: anthropicCol1,
|
||||
col2: anthropicCol2,
|
||||
},
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Anthropic}`]: {
|
||||
col1: bedrockAnthropicCol1,
|
||||
col2: bedrockAnthropicCol2,
|
||||
},
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.MistralAI}`]: {
|
||||
col1: bedrockMistralCol1,
|
||||
col2: bedrockMistralCol2,
|
||||
},
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Cohere}`]: {
|
||||
col1: bedrockCohereCol1,
|
||||
col2: bedrockCohereCol2,
|
||||
},
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Meta}`]: bedrockGeneralColumns,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.AI21}`]: bedrockGeneralColumns,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Amazon}`]: bedrockGeneralColumns,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.DeepSeek}`]: bedrockGeneralColumns,
|
||||
[EModelEndpoint.google]: {
|
||||
col1: googleCol1,
|
||||
col2: googleCol2,
|
||||
},
|
||||
};
|
||||
|
||||
export const agentParamSettings: Record<string, SettingsConfiguration | undefined> = Object.entries(
|
||||
presetSettings,
|
||||
).reduce<Record<string, SettingsConfiguration | undefined>>((acc, [key, value]) => {
|
||||
if (value) {
|
||||
acc[key] = value.col2;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
|
@ -28,6 +28,10 @@ export enum PermissionTypes {
|
|||
* Type for using the "Run Code" LC Code Interpreter API feature
|
||||
*/
|
||||
RUN_CODE = 'RUN_CODE',
|
||||
/**
|
||||
* Type for using the "Web Search" feature
|
||||
*/
|
||||
WEB_SEARCH = 'WEB_SEARCH',
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -79,6 +83,11 @@ export const runCodePermissionsSchema = z.object({
|
|||
});
|
||||
export type TRunCodePermissions = z.infer<typeof runCodePermissionsSchema>;
|
||||
|
||||
export const webSearchPermissionsSchema = z.object({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
});
|
||||
export type TWebSearchPermissions = z.infer<typeof webSearchPermissionsSchema>;
|
||||
|
||||
// Define a single permissions schema that holds all permission types.
|
||||
export const permissionsSchema = z.object({
|
||||
[PermissionTypes.PROMPTS]: promptPermissionsSchema,
|
||||
|
|
@ -87,4 +96,5 @@ export const permissionsSchema = z.object({
|
|||
[PermissionTypes.MULTI_CONVO]: multiConvoPermissionsSchema,
|
||||
[PermissionTypes.TEMPORARY_CHAT]: temporaryChatPermissionsSchema,
|
||||
[PermissionTypes.RUN_CODE]: runCodePermissionsSchema,
|
||||
[PermissionTypes.WEB_SEARCH]: webSearchPermissionsSchema,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
agentPermissionsSchema,
|
||||
promptPermissionsSchema,
|
||||
runCodePermissionsSchema,
|
||||
webSearchPermissionsSchema,
|
||||
bookmarkPermissionsSchema,
|
||||
multiConvoPermissionsSchema,
|
||||
temporaryChatPermissionsSchema,
|
||||
|
|
@ -62,6 +63,9 @@ const defaultRolesSchema = z.object({
|
|||
[PermissionTypes.RUN_CODE]: runCodePermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.WEB_SEARCH]: webSearchPermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
[SystemRoles.USER]: roleSchema.extend({
|
||||
|
|
@ -96,6 +100,9 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[PermissionTypes.RUN_CODE]: {
|
||||
[Permissions.USE]: true,
|
||||
},
|
||||
[PermissionTypes.WEB_SEARCH]: {
|
||||
[Permissions.USE]: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
[SystemRoles.USER]: {
|
||||
|
|
@ -107,6 +114,7 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[PermissionTypes.MULTI_CONVO]: {},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: {},
|
||||
[PermissionTypes.RUN_CODE]: {},
|
||||
[PermissionTypes.WEB_SEARCH]: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { z } from 'zod';
|
||||
import { Tools } from './types/assistants';
|
||||
import type { TMessageContentParts, FunctionTool, FunctionToolCall } from './types/assistants';
|
||||
import type { SearchResultData } from './types/web';
|
||||
import type { TEphemeralAgent } from './types';
|
||||
import type { TFile } from './types/files';
|
||||
|
||||
|
|
@ -101,7 +102,8 @@ export const isEphemeralAgent = (
|
|||
}
|
||||
const hasMCPSelected = (ephemeralAgent?.mcp?.length ?? 0) > 0;
|
||||
const hasCodeSelected = (ephemeralAgent?.execute_code ?? false) === true;
|
||||
return hasMCPSelected || hasCodeSelected;
|
||||
const hasSearchSelected = (ephemeralAgent?.web_search ?? false) === true;
|
||||
return hasMCPSelected || hasCodeSelected || hasSearchSelected;
|
||||
};
|
||||
|
||||
export const isParamEndpoint = (
|
||||
|
|
@ -177,6 +179,7 @@ export const defaultAgentFormValues = {
|
|||
recursion_limit: undefined,
|
||||
[Tools.execute_code]: false,
|
||||
[Tools.file_search]: false,
|
||||
[Tools.web_search]: false,
|
||||
};
|
||||
|
||||
export const ImageVisionTool: FunctionTool = {
|
||||
|
|
@ -517,7 +520,13 @@ export const tMessageSchema = z.object({
|
|||
iconURL: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export type TAttachmentMetadata = { messageId: string; toolCallId: string };
|
||||
export type TAttachmentMetadata = {
|
||||
type?: Tools;
|
||||
messageId: string;
|
||||
toolCallId: string;
|
||||
[Tools.web_search]?: SearchResultData;
|
||||
};
|
||||
|
||||
export type TAttachment =
|
||||
| (TFile & TAttachmentMetadata)
|
||||
| (Pick<TFile, 'filename' | 'filepath' | 'conversationId'> & {
|
||||
|
|
@ -745,6 +754,7 @@ export type TSetOption = (
|
|||
|
||||
export type TConversation = z.infer<typeof tConversationSchema> & {
|
||||
presetOverride?: Partial<TPreset>;
|
||||
disableParams?: boolean;
|
||||
};
|
||||
|
||||
export const tSharedLinkSchema = z.object({
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import type {
|
|||
TConversationTag,
|
||||
TBanner,
|
||||
} from './schemas';
|
||||
import { SettingDefinition } from './generate';
|
||||
export type TOpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam;
|
||||
|
||||
export * from './schemas';
|
||||
|
|
@ -43,6 +44,7 @@ export type TEndpointOption = {
|
|||
|
||||
export type TEphemeralAgent = {
|
||||
mcp?: string[];
|
||||
web_search?: boolean;
|
||||
execute_code?: boolean;
|
||||
};
|
||||
|
||||
|
|
@ -78,7 +80,7 @@ export type EventSubmission = Omit<TSubmission, 'initialResponse'> & { initialRe
|
|||
export type TPluginAction = {
|
||||
pluginKey: string;
|
||||
action: 'install' | 'uninstall';
|
||||
auth?: unknown;
|
||||
auth?: Partial<Record<string, string>>;
|
||||
isEntityTool?: boolean;
|
||||
};
|
||||
|
||||
|
|
@ -88,7 +90,7 @@ export type TUpdateUserPlugins = {
|
|||
isEntityTool?: boolean;
|
||||
pluginKey: string;
|
||||
action: string;
|
||||
auth?: unknown;
|
||||
auth?: Partial<Record<string, string | null>>;
|
||||
};
|
||||
|
||||
// TODO `label` needs to be changed to the proper `TranslationKeys`
|
||||
|
|
@ -268,6 +270,10 @@ export type TConfig = {
|
|||
disableBuilder?: boolean;
|
||||
retrievalModels?: string[];
|
||||
capabilities?: string[];
|
||||
customParams?: {
|
||||
defaultParamsEndpoint?: string;
|
||||
paramDefinitions?: SettingDefinition[];
|
||||
};
|
||||
};
|
||||
|
||||
export type TEndpointsConfig =
|
||||
|
|
@ -540,3 +546,13 @@ export type TAcceptTermsResponse = {
|
|||
};
|
||||
|
||||
export type TBannerResponse = TBanner | null;
|
||||
|
||||
export type TBalanceResponse = {
|
||||
tokenCredits: number;
|
||||
// Automatic refill settings
|
||||
autoRefillEnabled: boolean;
|
||||
refillIntervalValue?: number;
|
||||
refillIntervalUnit?: 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months';
|
||||
lastRefill?: Date;
|
||||
refillAmount?: number;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export enum Tools {
|
|||
execute_code = 'execute_code',
|
||||
code_interpreter = 'code_interpreter',
|
||||
file_search = 'file_search',
|
||||
web_search = 'web_search',
|
||||
retrieval = 'retrieval',
|
||||
function = 'function',
|
||||
}
|
||||
|
|
@ -222,6 +223,7 @@ export type Agent = {
|
|||
hide_sequential_outputs?: boolean;
|
||||
artifacts?: ArtifactModes;
|
||||
recursion_limit?: number;
|
||||
version?: number;
|
||||
};
|
||||
|
||||
export type TAgentsMap = Record<string, Agent | undefined>;
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ export type BatchFile = {
|
|||
filepath: string;
|
||||
embedded: boolean;
|
||||
source: FileSources;
|
||||
temp_file_id?: string;
|
||||
};
|
||||
|
||||
export type DeleteFilesBody = {
|
||||
|
|
|
|||
|
|
@ -129,7 +129,20 @@ export type UpdateAgentVariables = {
|
|||
data: AgentUpdateParams;
|
||||
};
|
||||
|
||||
export type UpdateAgentMutationOptions = MutationOptions<Agent, UpdateAgentVariables>;
|
||||
export type DuplicateVersionError = Error & {
|
||||
statusCode?: number;
|
||||
details?: {
|
||||
duplicateVersion?: unknown;
|
||||
versionIndex?: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type UpdateAgentMutationOptions = MutationOptions<
|
||||
Agent,
|
||||
UpdateAgentVariables,
|
||||
unknown,
|
||||
DuplicateVersionError
|
||||
>;
|
||||
|
||||
export type DuplicateAgentBody = {
|
||||
agent_id: string;
|
||||
|
|
@ -159,6 +172,13 @@ export type DeleteAgentActionVariables = {
|
|||
|
||||
export type DeleteAgentActionOptions = MutationOptions<void, DeleteAgentActionVariables>;
|
||||
|
||||
export type RevertAgentVersionVariables = {
|
||||
agent_id: string;
|
||||
version_index: number;
|
||||
};
|
||||
|
||||
export type RevertAgentVersionOptions = MutationOptions<Agent, RevertAgentVersionVariables>;
|
||||
|
||||
export type DeleteConversationOptions = MutationOptions<
|
||||
types.TDeleteConversationResponse,
|
||||
types.TDeleteConversationRequest
|
||||
|
|
|
|||
|
|
@ -101,7 +101,11 @@ export type AllPromptGroupsResponse = t.TPromptGroup[];
|
|||
export type ConversationTagsResponse = s.TConversationTag[];
|
||||
|
||||
export type VerifyToolAuthParams = { toolId: string };
|
||||
export type VerifyToolAuthResponse = { authenticated: boolean; message?: string | s.AuthType };
|
||||
export type VerifyToolAuthResponse = {
|
||||
authenticated: boolean;
|
||||
message?: string | s.AuthType;
|
||||
authTypes?: [string, s.AuthType][];
|
||||
};
|
||||
|
||||
export type GetToolCallParams = { conversationId: string };
|
||||
export type ToolCallResults = a.ToolCallResult[];
|
||||
|
|
|
|||
593
packages/data-provider/src/types/web.ts
Normal file
593
packages/data-provider/src/types/web.ts
Normal file
|
|
@ -0,0 +1,593 @@
|
|||
import type { Logger as WinstonLogger } from 'winston';
|
||||
import type { RunnableConfig } from '@langchain/core/runnables';
|
||||
|
||||
export type SearchRefType = 'search' | 'image' | 'news' | 'video' | 'ref';
|
||||
|
||||
export enum DATE_RANGE {
|
||||
PAST_HOUR = 'h',
|
||||
PAST_24_HOURS = 'd',
|
||||
PAST_WEEK = 'w',
|
||||
PAST_MONTH = 'm',
|
||||
PAST_YEAR = 'y',
|
||||
}
|
||||
|
||||
export type SearchProvider = 'serper' | 'searxng';
|
||||
export type RerankerType = 'infinity' | 'jina' | 'cohere' | 'none';
|
||||
|
||||
export interface Highlight {
|
||||
score: number;
|
||||
text: string;
|
||||
references?: UsedReferences;
|
||||
}
|
||||
|
||||
export type ProcessedSource = {
|
||||
content?: string;
|
||||
attribution?: string;
|
||||
references?: References;
|
||||
highlights?: Highlight[];
|
||||
processed?: boolean;
|
||||
};
|
||||
|
||||
export type ProcessedOrganic = OrganicResult & ProcessedSource;
|
||||
export type ProcessedTopStory = TopStoryResult & ProcessedSource;
|
||||
export type ValidSource = ProcessedOrganic | ProcessedTopStory;
|
||||
|
||||
export type ResultReference = {
|
||||
link: string;
|
||||
type: 'link' | 'image' | 'video';
|
||||
title?: string;
|
||||
attribution?: string;
|
||||
};
|
||||
export interface SearchResultData {
|
||||
turn?: number;
|
||||
organic?: ProcessedOrganic[];
|
||||
topStories?: ProcessedTopStory[];
|
||||
images?: ImageResult[];
|
||||
videos?: VideoResult[];
|
||||
places?: PlaceResult[];
|
||||
news?: NewsResult[];
|
||||
shopping?: ShoppingResult[];
|
||||
knowledgeGraph?: KnowledgeGraphResult;
|
||||
answerBox?: AnswerBoxResult;
|
||||
peopleAlsoAsk?: PeopleAlsoAskResult[];
|
||||
relatedSearches?: Array<{ query: string }>;
|
||||
references?: ResultReference[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
data?: SearchResultData;
|
||||
error?: string;
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface Source {
|
||||
link: string;
|
||||
html?: string;
|
||||
title?: string;
|
||||
snippet?: string;
|
||||
date?: string;
|
||||
}
|
||||
|
||||
export interface SearchConfig {
|
||||
searchProvider?: SearchProvider;
|
||||
serperApiKey?: string;
|
||||
searxngInstanceUrl?: string;
|
||||
searxngApiKey?: string;
|
||||
}
|
||||
|
||||
export type References = {
|
||||
links: MediaReference[];
|
||||
images: MediaReference[];
|
||||
videos: MediaReference[];
|
||||
};
|
||||
export interface ScrapeResult {
|
||||
url: string;
|
||||
error?: boolean;
|
||||
content: string;
|
||||
attribution?: string;
|
||||
references?: References;
|
||||
highlights?: Highlight[];
|
||||
}
|
||||
|
||||
export interface ProcessSourcesConfig {
|
||||
topResults?: number;
|
||||
strategies?: string[];
|
||||
filterContent?: boolean;
|
||||
reranker?: unknown;
|
||||
logger?: Logger;
|
||||
}
|
||||
|
||||
export interface FirecrawlConfig {
|
||||
firecrawlApiKey?: string;
|
||||
firecrawlApiUrl?: string;
|
||||
firecrawlFormats?: string[];
|
||||
}
|
||||
|
||||
export interface ScraperContentResult {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ScraperExtractionResult {
|
||||
no_extraction: ScraperContentResult;
|
||||
}
|
||||
|
||||
export interface JinaRerankerResult {
|
||||
index: number;
|
||||
relevance_score: number;
|
||||
document?: string | { text: string };
|
||||
}
|
||||
|
||||
export interface JinaRerankerResponse {
|
||||
model: string;
|
||||
usage: {
|
||||
total_tokens: number;
|
||||
};
|
||||
results: JinaRerankerResult[];
|
||||
}
|
||||
|
||||
export interface CohereRerankerResult {
|
||||
index: number;
|
||||
relevance_score: number;
|
||||
}
|
||||
|
||||
export interface CohereRerankerResponse {
|
||||
results: CohereRerankerResult[];
|
||||
id: string;
|
||||
meta: {
|
||||
api_version: {
|
||||
version: string;
|
||||
is_experimental: boolean;
|
||||
};
|
||||
billed_units: {
|
||||
search_units: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type SafeSearchLevel = 0 | 1 | 2;
|
||||
|
||||
export type Logger = WinstonLogger;
|
||||
export interface SearchToolConfig extends SearchConfig, ProcessSourcesConfig, FirecrawlConfig {
|
||||
logger?: Logger;
|
||||
safeSearch?: SafeSearchLevel;
|
||||
jinaApiKey?: string;
|
||||
cohereApiKey?: string;
|
||||
rerankerType?: RerankerType;
|
||||
onSearchResults?: (results: SearchResult, runnableConfig?: RunnableConfig) => void;
|
||||
onGetHighlights?: (link: string) => void;
|
||||
}
|
||||
export interface MediaReference {
|
||||
originalUrl: string;
|
||||
title?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
export type UsedReferences = {
|
||||
type: 'link' | 'image' | 'video';
|
||||
originalIndex: number;
|
||||
reference: MediaReference;
|
||||
}[];
|
||||
|
||||
/** Firecrawl */
|
||||
|
||||
export interface FirecrawlScrapeOptions {
|
||||
formats?: string[];
|
||||
includeTags?: string[];
|
||||
excludeTags?: string[];
|
||||
headers?: Record<string, string>;
|
||||
waitFor?: number;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export interface ScrapeMetadata {
|
||||
// Core source information
|
||||
sourceURL?: string;
|
||||
url?: string;
|
||||
scrapeId?: string;
|
||||
statusCode?: number;
|
||||
// Basic metadata
|
||||
title?: string;
|
||||
description?: string;
|
||||
language?: string;
|
||||
favicon?: string;
|
||||
viewport?: string;
|
||||
robots?: string;
|
||||
'theme-color'?: string;
|
||||
// Open Graph metadata
|
||||
'og:url'?: string;
|
||||
'og:title'?: string;
|
||||
'og:description'?: string;
|
||||
'og:type'?: string;
|
||||
'og:image'?: string;
|
||||
'og:image:width'?: string;
|
||||
'og:image:height'?: string;
|
||||
'og:site_name'?: string;
|
||||
ogUrl?: string;
|
||||
ogTitle?: string;
|
||||
ogDescription?: string;
|
||||
ogImage?: string;
|
||||
ogSiteName?: string;
|
||||
// Article metadata
|
||||
'article:author'?: string;
|
||||
'article:published_time'?: string;
|
||||
'article:modified_time'?: string;
|
||||
'article:section'?: string;
|
||||
'article:tag'?: string;
|
||||
'article:publisher'?: string;
|
||||
publishedTime?: string;
|
||||
modifiedTime?: string;
|
||||
// Twitter metadata
|
||||
'twitter:site'?: string | boolean | number | null;
|
||||
'twitter:creator'?: string;
|
||||
'twitter:card'?: string;
|
||||
'twitter:image'?: string;
|
||||
'twitter:dnt'?: string;
|
||||
'twitter:app:name:iphone'?: string;
|
||||
'twitter:app:id:iphone'?: string;
|
||||
'twitter:app:url:iphone'?: string;
|
||||
'twitter:app:name:ipad'?: string;
|
||||
'twitter:app:id:ipad'?: string;
|
||||
'twitter:app:url:ipad'?: string;
|
||||
'twitter:app:name:googleplay'?: string;
|
||||
'twitter:app:id:googleplay'?: string;
|
||||
'twitter:app:url:googleplay'?: string;
|
||||
// Facebook metadata
|
||||
'fb:app_id'?: string;
|
||||
// App links
|
||||
'al:ios:url'?: string;
|
||||
'al:ios:app_name'?: string;
|
||||
'al:ios:app_store_id'?: string;
|
||||
// Allow for additional properties that might be present
|
||||
[key: string]: string | number | boolean | null | undefined;
|
||||
}
|
||||
|
||||
export interface FirecrawlScrapeResponse {
|
||||
success: boolean;
|
||||
data?: {
|
||||
markdown?: string;
|
||||
html?: string;
|
||||
rawHtml?: string;
|
||||
screenshot?: string;
|
||||
links?: string[];
|
||||
metadata?: ScrapeMetadata;
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface FirecrawlScraperConfig {
|
||||
apiKey?: string;
|
||||
apiUrl?: string;
|
||||
formats?: string[];
|
||||
timeout?: number;
|
||||
logger?: Logger;
|
||||
}
|
||||
|
||||
export type GetSourcesParams = {
|
||||
query: string;
|
||||
date?: DATE_RANGE;
|
||||
country?: string;
|
||||
numResults?: number;
|
||||
safeSearch?: SearchToolConfig['safeSearch'];
|
||||
images?: boolean;
|
||||
videos?: boolean;
|
||||
news?: boolean;
|
||||
type?: 'search' | 'images' | 'videos' | 'news';
|
||||
};
|
||||
|
||||
/** Serper API */
|
||||
export interface VideoResult {
|
||||
title?: string;
|
||||
link?: string;
|
||||
snippet?: string;
|
||||
imageUrl?: string;
|
||||
duration?: string;
|
||||
source?: string;
|
||||
channel?: string;
|
||||
date?: string;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export interface PlaceResult {
|
||||
position?: number;
|
||||
name?: string;
|
||||
address?: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
rating?: number;
|
||||
ratingCount?: number;
|
||||
category?: string;
|
||||
identifier?: string;
|
||||
}
|
||||
|
||||
export interface NewsResult {
|
||||
title?: string;
|
||||
link?: string;
|
||||
snippet?: string;
|
||||
date?: string;
|
||||
source?: string;
|
||||
imageUrl?: string;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export interface ShoppingResult {
|
||||
title?: string;
|
||||
source?: string;
|
||||
link?: string;
|
||||
price?: string;
|
||||
delivery?: string;
|
||||
imageUrl?: string;
|
||||
rating?: number;
|
||||
ratingCount?: number;
|
||||
offers?: string;
|
||||
productId?: string;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export interface ScholarResult {
|
||||
title?: string;
|
||||
link?: string;
|
||||
publicationInfo?: string;
|
||||
snippet?: string;
|
||||
year?: number;
|
||||
citedBy?: number;
|
||||
}
|
||||
|
||||
export interface ImageResult {
|
||||
title?: string;
|
||||
imageUrl?: string;
|
||||
imageWidth?: number;
|
||||
imageHeight?: number;
|
||||
thumbnailUrl?: string;
|
||||
thumbnailWidth?: number;
|
||||
thumbnailHeight?: number;
|
||||
source?: string;
|
||||
domain?: string;
|
||||
link?: string;
|
||||
googleUrl?: string;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export interface SerperSearchPayload extends SerperSearchInput {
|
||||
/**
|
||||
* Search type/vertical
|
||||
* Options: "search" (web), "images", "news", "places", "videos"
|
||||
*/
|
||||
type?: 'search' | 'images' | 'news' | 'places' | 'videos';
|
||||
|
||||
/**
|
||||
* Starting index for search results pagination (used instead of page)
|
||||
*/
|
||||
start?: number;
|
||||
|
||||
/**
|
||||
* Filtering for safe search
|
||||
* Options: "off", "moderate", "active"
|
||||
*/
|
||||
safe?: 'off' | 'moderate' | 'active';
|
||||
}
|
||||
|
||||
export type SerperSearchParameters = Pick<SerperSearchPayload, 'q' | 'type'> & {
|
||||
engine: 'google';
|
||||
};
|
||||
|
||||
export interface OrganicResult {
|
||||
position?: number;
|
||||
title?: string;
|
||||
link: string;
|
||||
snippet?: string;
|
||||
date?: string;
|
||||
sitelinks?: Array<{
|
||||
title: string;
|
||||
link: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface TopStoryResult {
|
||||
title?: string;
|
||||
link: string;
|
||||
source?: string;
|
||||
date?: string;
|
||||
imageUrl?: string;
|
||||
}
|
||||
export interface KnowledgeGraphResult {
|
||||
title?: string;
|
||||
type?: string;
|
||||
imageUrl?: string;
|
||||
description?: string;
|
||||
descriptionSource?: string;
|
||||
descriptionLink?: string;
|
||||
attributes?: Record<string, string>;
|
||||
website?: string;
|
||||
}
|
||||
|
||||
export interface AnswerBoxResult {
|
||||
title?: string;
|
||||
snippet?: string;
|
||||
snippetHighlighted?: string[];
|
||||
link?: string;
|
||||
date?: string;
|
||||
}
|
||||
|
||||
export interface PeopleAlsoAskResult {
|
||||
question?: string;
|
||||
snippet?: string;
|
||||
title?: string;
|
||||
link?: string;
|
||||
}
|
||||
|
||||
export type RelatedSearches = Array<{ query: string }>;
|
||||
|
||||
export interface SerperSearchInput {
|
||||
/**
|
||||
* The search query string
|
||||
*/
|
||||
q: string;
|
||||
|
||||
/**
|
||||
* Country code for localized results
|
||||
* Examples: "us", "uk", "ca", "de", etc.
|
||||
*/
|
||||
gl?: string;
|
||||
|
||||
/**
|
||||
* Interface language
|
||||
* Examples: "en", "fr", "de", etc.
|
||||
*/
|
||||
hl?: string;
|
||||
|
||||
/**
|
||||
* Number of results to return (up to 100)
|
||||
*/
|
||||
num?: number;
|
||||
/**
|
||||
* Specific location for contextual results
|
||||
* Example: "New York, NY"
|
||||
*/
|
||||
location?: string;
|
||||
|
||||
/**
|
||||
* Search autocorrection setting
|
||||
*/
|
||||
autocorrect?: boolean;
|
||||
page?: number;
|
||||
/**
|
||||
* Date range for search results
|
||||
* Options: "h" (past hour), "d" (past 24 hours), "w" (past week),
|
||||
* "m" (past month), "y" (past year)
|
||||
* `qdr:${DATE_RANGE}`
|
||||
*/
|
||||
tbs?: string;
|
||||
}
|
||||
|
||||
export type SerperResultData = {
|
||||
searchParameters: SerperSearchPayload;
|
||||
organic?: OrganicResult[];
|
||||
topStories?: TopStoryResult[];
|
||||
images?: ImageResult[];
|
||||
videos?: VideoResult[];
|
||||
places?: PlaceResult[];
|
||||
news?: NewsResult[];
|
||||
shopping?: ShoppingResult[];
|
||||
peopleAlsoAsk?: PeopleAlsoAskResult[];
|
||||
relatedSearches?: RelatedSearches;
|
||||
knowledgeGraph?: KnowledgeGraphResult;
|
||||
answerBox?: AnswerBoxResult;
|
||||
credits?: number;
|
||||
};
|
||||
|
||||
/** SearXNG */
|
||||
|
||||
export interface SearxNGSearchPayload {
|
||||
/**
|
||||
* The search query string
|
||||
* Supports syntax specific to different search engines
|
||||
* Example: "site:github.com SearXNG"
|
||||
*/
|
||||
q: string;
|
||||
|
||||
/**
|
||||
* Comma-separated list of search categories
|
||||
* Example: "general,images,news"
|
||||
*/
|
||||
categories?: string;
|
||||
|
||||
/**
|
||||
* Comma-separated list of search engines to use
|
||||
* Example: "google,bing,duckduckgo"
|
||||
*/
|
||||
engines?: string;
|
||||
|
||||
/**
|
||||
* Code of the language for search results
|
||||
* Example: "en", "fr", "de", "es"
|
||||
*/
|
||||
language?: string;
|
||||
|
||||
/**
|
||||
* Search page number
|
||||
* Default: 1
|
||||
*/
|
||||
pageno?: number;
|
||||
|
||||
/**
|
||||
* Time range filter for search results
|
||||
* Options: "day", "month", "year"
|
||||
*/
|
||||
time_range?: 'day' | 'month' | 'year';
|
||||
|
||||
/**
|
||||
* Output format of results
|
||||
* Options: "json", "csv", "rss"
|
||||
*/
|
||||
format?: 'json' | 'csv' | 'rss';
|
||||
|
||||
/**
|
||||
* Open search results on new tab
|
||||
* Options: `0` (off), `1` (on)
|
||||
*/
|
||||
results_on_new_tab?: 0 | 1;
|
||||
|
||||
/**
|
||||
* Proxy image results through SearxNG
|
||||
* Options: true, false
|
||||
*/
|
||||
image_proxy?: boolean;
|
||||
|
||||
/**
|
||||
* Service for autocomplete suggestions
|
||||
* Options: "google", "dbpedia", "duckduckgo", "mwmbl",
|
||||
* "startpage", "wikipedia", "stract", "swisscows", "qwant"
|
||||
*/
|
||||
autocomplete?: string;
|
||||
|
||||
/**
|
||||
* Safe search filtering level
|
||||
* Options: "0" (off), "1" (moderate), "2" (strict)
|
||||
*/
|
||||
safesearch?: 0 | 1 | 2;
|
||||
|
||||
/**
|
||||
* Theme to use for results page
|
||||
* Default: "simple" (other themes may be available per instance)
|
||||
*/
|
||||
theme?: string;
|
||||
|
||||
/**
|
||||
* List of enabled plugins
|
||||
* Default: "Hash_plugin,Self_Information,Tracker_URL_remover,Ahmia_blacklist"
|
||||
*/
|
||||
enabled_plugins?: string;
|
||||
|
||||
/**
|
||||
* List of disabled plugins
|
||||
*/
|
||||
disabled_plugins?: string;
|
||||
|
||||
/**
|
||||
* List of enabled engines
|
||||
*/
|
||||
enabled_engines?: string;
|
||||
|
||||
/**
|
||||
* List of disabled engines
|
||||
*/
|
||||
disabled_engines?: string;
|
||||
}
|
||||
|
||||
export interface SearXNGResult {
|
||||
title?: string;
|
||||
url?: string;
|
||||
content?: string;
|
||||
publishedDate?: string;
|
||||
img_src?: string;
|
||||
}
|
||||
|
||||
export type ProcessSourcesFields = {
|
||||
result: SearchResult;
|
||||
numElements: number;
|
||||
query: string;
|
||||
news: boolean;
|
||||
proMode: boolean;
|
||||
onGetHighlights: SearchToolConfig['onGetHighlights'];
|
||||
};
|
||||
|
|
@ -1,5 +1,15 @@
|
|||
export const envVarRegex = /^\${(.+)}$/;
|
||||
|
||||
/** Extracts the environment variable name from a template literal string */
|
||||
export function extractVariableName(value: string): string | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = value.trim().match(envVarRegex);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
/** Extracts the value of an environment variable from a string. */
|
||||
export function extractEnvVariable(value: string) {
|
||||
if (!value) {
|
||||
|
|
|
|||
271
packages/data-provider/src/web.ts
Normal file
271
packages/data-provider/src/web.ts
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
import type {
|
||||
ScraperTypes,
|
||||
RerankerTypes,
|
||||
TCustomConfig,
|
||||
SearchProviders,
|
||||
TWebSearchConfig,
|
||||
} from './config';
|
||||
import { extractVariableName } from './utils';
|
||||
import { SearchCategories, SafeSearchTypes } from './config';
|
||||
import { AuthType } from './schemas';
|
||||
|
||||
export function loadWebSearchConfig(
|
||||
config: TCustomConfig['webSearch'],
|
||||
): TCustomConfig['webSearch'] {
|
||||
const serperApiKey = config?.serperApiKey ?? '${SERPER_API_KEY}';
|
||||
const firecrawlApiKey = config?.firecrawlApiKey ?? '${FIRECRAWL_API_KEY}';
|
||||
const firecrawlApiUrl = config?.firecrawlApiUrl ?? '${FIRECRAWL_API_URL}';
|
||||
const jinaApiKey = config?.jinaApiKey ?? '${JINA_API_KEY}';
|
||||
const cohereApiKey = config?.cohereApiKey ?? '${COHERE_API_KEY}';
|
||||
const safeSearch = config?.safeSearch ?? SafeSearchTypes.MODERATE;
|
||||
|
||||
return {
|
||||
...config,
|
||||
safeSearch,
|
||||
jinaApiKey,
|
||||
cohereApiKey,
|
||||
serperApiKey,
|
||||
firecrawlApiKey,
|
||||
firecrawlApiUrl,
|
||||
};
|
||||
}
|
||||
|
||||
export type TWebSearchKeys =
|
||||
| 'serperApiKey'
|
||||
| 'firecrawlApiKey'
|
||||
| 'firecrawlApiUrl'
|
||||
| 'jinaApiKey'
|
||||
| 'cohereApiKey';
|
||||
|
||||
export type TWebSearchCategories =
|
||||
| SearchCategories.PROVIDERS
|
||||
| SearchCategories.SCRAPERS
|
||||
| SearchCategories.RERANKERS;
|
||||
|
||||
export const webSearchAuth = {
|
||||
providers: {
|
||||
serper: {
|
||||
serperApiKey: 1 as const,
|
||||
},
|
||||
},
|
||||
scrapers: {
|
||||
firecrawl: {
|
||||
firecrawlApiKey: 1 as const,
|
||||
/** Optional (0) */
|
||||
firecrawlApiUrl: 0 as const,
|
||||
},
|
||||
},
|
||||
rerankers: {
|
||||
jina: { jinaApiKey: 1 as const },
|
||||
cohere: { cohereApiKey: 1 as const },
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts all API keys from the webSearchAuth configuration object
|
||||
*/
|
||||
export const webSearchKeys: TWebSearchKeys[] = [];
|
||||
|
||||
// Iterate through each category (providers, scrapers, rerankers)
|
||||
for (const category of Object.keys(webSearchAuth)) {
|
||||
const categoryObj = webSearchAuth[category as TWebSearchCategories];
|
||||
|
||||
// Iterate through each service within the category
|
||||
for (const service of Object.keys(categoryObj)) {
|
||||
const serviceObj = categoryObj[service as keyof typeof categoryObj];
|
||||
|
||||
// Extract the API keys from the service
|
||||
for (const key of Object.keys(serviceObj)) {
|
||||
webSearchKeys.push(key as TWebSearchKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function extractWebSearchEnvVars({
|
||||
keys,
|
||||
config,
|
||||
}: {
|
||||
keys: TWebSearchKeys[];
|
||||
config: TCustomConfig['webSearch'] | undefined;
|
||||
}): string[] {
|
||||
if (!config) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const authFields: string[] = [];
|
||||
const relevantKeys = keys.filter((k) => k in config);
|
||||
|
||||
for (const key of relevantKeys) {
|
||||
const value = config[key];
|
||||
if (typeof value === 'string') {
|
||||
const varName = extractVariableName(value);
|
||||
if (varName) {
|
||||
authFields.push(varName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return authFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type for web search authentication result
|
||||
*/
|
||||
export interface WebSearchAuthResult {
|
||||
/** Whether all required categories have at least one authenticated service */
|
||||
authenticated: boolean;
|
||||
/** Authentication type (user_provided or system_defined) by category */
|
||||
authTypes: [TWebSearchCategories, AuthType][];
|
||||
/** Original authentication values mapped to their respective keys */
|
||||
authResult: Partial<TWebSearchConfig>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and verifies web search authentication values
|
||||
* @param params - Authentication parameters
|
||||
* @returns Authentication result
|
||||
*/
|
||||
export async function loadWebSearchAuth({
|
||||
userId,
|
||||
webSearchConfig,
|
||||
loadAuthValues,
|
||||
throwError = true,
|
||||
}: {
|
||||
userId: string;
|
||||
webSearchConfig: TCustomConfig['webSearch'];
|
||||
loadAuthValues: (params: {
|
||||
userId: string;
|
||||
authFields: string[];
|
||||
optional?: Set<string>;
|
||||
throwError?: boolean;
|
||||
}) => Promise<Record<string, string>>;
|
||||
throwError?: boolean;
|
||||
}): Promise<WebSearchAuthResult> {
|
||||
let authenticated = true;
|
||||
const authResult: Partial<TWebSearchConfig> = {};
|
||||
|
||||
/** Type-safe iterator for the category-service combinations */
|
||||
async function checkAuth<C extends TWebSearchCategories>(
|
||||
category: C,
|
||||
): Promise<[boolean, boolean]> {
|
||||
type ServiceType = keyof (typeof webSearchAuth)[C];
|
||||
let isUserProvided = false;
|
||||
|
||||
// Check if a specific service is specified in the config
|
||||
let specificService: ServiceType | undefined;
|
||||
if (category === SearchCategories.PROVIDERS && webSearchConfig?.searchProvider) {
|
||||
specificService = webSearchConfig.searchProvider as unknown as ServiceType;
|
||||
} else if (category === SearchCategories.SCRAPERS && webSearchConfig?.scraperType) {
|
||||
specificService = webSearchConfig.scraperType as unknown as ServiceType;
|
||||
} else if (category === SearchCategories.RERANKERS && webSearchConfig?.rerankerType) {
|
||||
specificService = webSearchConfig.rerankerType as unknown as ServiceType;
|
||||
}
|
||||
|
||||
// If a specific service is specified, only check that one
|
||||
const services = specificService
|
||||
? [specificService]
|
||||
: (Object.keys(webSearchAuth[category]) as ServiceType[]);
|
||||
|
||||
for (const service of services) {
|
||||
// Skip if the service doesn't exist in the webSearchAuth config
|
||||
if (!webSearchAuth[category][service]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const serviceConfig = webSearchAuth[category][service];
|
||||
|
||||
// Split keys into required and optional
|
||||
const requiredKeys: TWebSearchKeys[] = [];
|
||||
const optionalKeys: TWebSearchKeys[] = [];
|
||||
|
||||
for (const key in serviceConfig) {
|
||||
const typedKey = key as TWebSearchKeys;
|
||||
if (serviceConfig[typedKey as keyof typeof serviceConfig] === 1) {
|
||||
requiredKeys.push(typedKey);
|
||||
} else if (serviceConfig[typedKey as keyof typeof serviceConfig] === 0) {
|
||||
optionalKeys.push(typedKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (requiredKeys.length === 0) continue;
|
||||
|
||||
const requiredAuthFields = extractWebSearchEnvVars({
|
||||
keys: requiredKeys,
|
||||
config: webSearchConfig,
|
||||
});
|
||||
const optionalAuthFields = extractWebSearchEnvVars({
|
||||
keys: optionalKeys,
|
||||
config: webSearchConfig,
|
||||
});
|
||||
if (requiredAuthFields.length !== requiredKeys.length) continue;
|
||||
|
||||
const allKeys = [...requiredKeys, ...optionalKeys];
|
||||
const allAuthFields = [...requiredAuthFields, ...optionalAuthFields];
|
||||
const optionalSet = new Set(optionalAuthFields);
|
||||
|
||||
try {
|
||||
const authValues = await loadAuthValues({
|
||||
userId,
|
||||
authFields: allAuthFields,
|
||||
optional: optionalSet,
|
||||
throwError,
|
||||
});
|
||||
|
||||
let allFieldsAuthenticated = true;
|
||||
for (let j = 0; j < allAuthFields.length; j++) {
|
||||
const field = allAuthFields[j];
|
||||
const value = authValues[field];
|
||||
const originalKey = allKeys[j];
|
||||
if (originalKey) authResult[originalKey] = value;
|
||||
if (!optionalSet.has(field) && !value) {
|
||||
allFieldsAuthenticated = false;
|
||||
break;
|
||||
}
|
||||
if (!isUserProvided && process.env[field] !== value) {
|
||||
isUserProvided = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allFieldsAuthenticated) {
|
||||
continue;
|
||||
}
|
||||
if (category === SearchCategories.PROVIDERS) {
|
||||
authResult.searchProvider = service as SearchProviders;
|
||||
} else if (category === SearchCategories.SCRAPERS) {
|
||||
authResult.scraperType = service as ScraperTypes;
|
||||
} else if (category === SearchCategories.RERANKERS) {
|
||||
authResult.rerankerType = service as RerankerTypes;
|
||||
}
|
||||
return [true, isUserProvided];
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return [false, isUserProvided];
|
||||
}
|
||||
|
||||
const categories = [
|
||||
SearchCategories.PROVIDERS,
|
||||
SearchCategories.SCRAPERS,
|
||||
SearchCategories.RERANKERS,
|
||||
] as const;
|
||||
const authTypes: [TWebSearchCategories, AuthType][] = [];
|
||||
for (const category of categories) {
|
||||
const [isCategoryAuthenticated, isUserProvided] = await checkAuth(category);
|
||||
if (!isCategoryAuthenticated) {
|
||||
authenticated = false;
|
||||
authTypes.push([category, AuthType.USER_PROVIDED]);
|
||||
continue;
|
||||
}
|
||||
authTypes.push([category, isUserProvided ? AuthType.USER_PROVIDED : AuthType.SYSTEM_DEFINED]);
|
||||
}
|
||||
|
||||
authResult.safeSearch = webSearchConfig?.safeSearch ?? SafeSearchTypes.MODERATE;
|
||||
authResult.scraperTimeout = webSearchConfig?.scraperTimeout ?? 7500;
|
||||
|
||||
return {
|
||||
authTypes,
|
||||
authResult,
|
||||
authenticated,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue