librechat-data-provider 0.7.82 → 0.7.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-query/index.es.js +1 -1
- package/dist/react-query/index.es.js.map +1 -1
- package/package.json +3 -1
- package/specs/bedrock.spec.ts +114 -0
- package/specs/mcp.spec.ts +101 -1
- package/specs/web.spec.ts +903 -0
- package/src/api-endpoints.ts +2 -0
- package/src/bedrock.ts +4 -1
- package/src/config.ts +96 -4
- package/src/data-service.ts +8 -0
- package/src/file-config.ts +6 -0
- package/src/generate.ts +6 -5
- package/src/index.ts +4 -0
- package/src/keys.ts +1 -0
- package/src/mcp.ts +29 -4
- package/src/parameterSettings.ts +726 -0
- package/src/permissions.ts +10 -0
- package/src/roles.ts +8 -0
- package/src/schemas.ts +12 -2
- package/src/types/assistants.ts +2 -0
- package/src/types/files.ts +1 -0
- package/src/types/mutations.ts +21 -1
- package/src/types/queries.ts +5 -1
- package/src/types/web.ts +593 -0
- package/src/types.ts +8 -2
- package/src/utils.ts +10 -0
- package/src/web.ts +271 -0
package/src/web.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ScraperTypes,
|
|
3
|
+
RerankerTypes,
|
|
4
|
+
TCustomConfig,
|
|
5
|
+
SearchProviders,
|
|
6
|
+
TWebSearchConfig,
|
|
7
|
+
} from './config';
|
|
8
|
+
import { extractVariableName } from './utils';
|
|
9
|
+
import { SearchCategories, SafeSearchTypes } from './config';
|
|
10
|
+
import { AuthType } from './schemas';
|
|
11
|
+
|
|
12
|
+
export function loadWebSearchConfig(
|
|
13
|
+
config: TCustomConfig['webSearch'],
|
|
14
|
+
): TCustomConfig['webSearch'] {
|
|
15
|
+
const serperApiKey = config?.serperApiKey ?? '${SERPER_API_KEY}';
|
|
16
|
+
const firecrawlApiKey = config?.firecrawlApiKey ?? '${FIRECRAWL_API_KEY}';
|
|
17
|
+
const firecrawlApiUrl = config?.firecrawlApiUrl ?? '${FIRECRAWL_API_URL}';
|
|
18
|
+
const jinaApiKey = config?.jinaApiKey ?? '${JINA_API_KEY}';
|
|
19
|
+
const cohereApiKey = config?.cohereApiKey ?? '${COHERE_API_KEY}';
|
|
20
|
+
const safeSearch = config?.safeSearch ?? SafeSearchTypes.MODERATE;
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
...config,
|
|
24
|
+
safeSearch,
|
|
25
|
+
jinaApiKey,
|
|
26
|
+
cohereApiKey,
|
|
27
|
+
serperApiKey,
|
|
28
|
+
firecrawlApiKey,
|
|
29
|
+
firecrawlApiUrl,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type TWebSearchKeys =
|
|
34
|
+
| 'serperApiKey'
|
|
35
|
+
| 'firecrawlApiKey'
|
|
36
|
+
| 'firecrawlApiUrl'
|
|
37
|
+
| 'jinaApiKey'
|
|
38
|
+
| 'cohereApiKey';
|
|
39
|
+
|
|
40
|
+
export type TWebSearchCategories =
|
|
41
|
+
| SearchCategories.PROVIDERS
|
|
42
|
+
| SearchCategories.SCRAPERS
|
|
43
|
+
| SearchCategories.RERANKERS;
|
|
44
|
+
|
|
45
|
+
export const webSearchAuth = {
|
|
46
|
+
providers: {
|
|
47
|
+
serper: {
|
|
48
|
+
serperApiKey: 1 as const,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
scrapers: {
|
|
52
|
+
firecrawl: {
|
|
53
|
+
firecrawlApiKey: 1 as const,
|
|
54
|
+
/** Optional (0) */
|
|
55
|
+
firecrawlApiUrl: 0 as const,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
rerankers: {
|
|
59
|
+
jina: { jinaApiKey: 1 as const },
|
|
60
|
+
cohere: { cohereApiKey: 1 as const },
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Extracts all API keys from the webSearchAuth configuration object
|
|
66
|
+
*/
|
|
67
|
+
export const webSearchKeys: TWebSearchKeys[] = [];
|
|
68
|
+
|
|
69
|
+
// Iterate through each category (providers, scrapers, rerankers)
|
|
70
|
+
for (const category of Object.keys(webSearchAuth)) {
|
|
71
|
+
const categoryObj = webSearchAuth[category as TWebSearchCategories];
|
|
72
|
+
|
|
73
|
+
// Iterate through each service within the category
|
|
74
|
+
for (const service of Object.keys(categoryObj)) {
|
|
75
|
+
const serviceObj = categoryObj[service as keyof typeof categoryObj];
|
|
76
|
+
|
|
77
|
+
// Extract the API keys from the service
|
|
78
|
+
for (const key of Object.keys(serviceObj)) {
|
|
79
|
+
webSearchKeys.push(key as TWebSearchKeys);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function extractWebSearchEnvVars({
|
|
85
|
+
keys,
|
|
86
|
+
config,
|
|
87
|
+
}: {
|
|
88
|
+
keys: TWebSearchKeys[];
|
|
89
|
+
config: TCustomConfig['webSearch'] | undefined;
|
|
90
|
+
}): string[] {
|
|
91
|
+
if (!config) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const authFields: string[] = [];
|
|
96
|
+
const relevantKeys = keys.filter((k) => k in config);
|
|
97
|
+
|
|
98
|
+
for (const key of relevantKeys) {
|
|
99
|
+
const value = config[key];
|
|
100
|
+
if (typeof value === 'string') {
|
|
101
|
+
const varName = extractVariableName(value);
|
|
102
|
+
if (varName) {
|
|
103
|
+
authFields.push(varName);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return authFields;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Type for web search authentication result
|
|
113
|
+
*/
|
|
114
|
+
export interface WebSearchAuthResult {
|
|
115
|
+
/** Whether all required categories have at least one authenticated service */
|
|
116
|
+
authenticated: boolean;
|
|
117
|
+
/** Authentication type (user_provided or system_defined) by category */
|
|
118
|
+
authTypes: [TWebSearchCategories, AuthType][];
|
|
119
|
+
/** Original authentication values mapped to their respective keys */
|
|
120
|
+
authResult: Partial<TWebSearchConfig>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Loads and verifies web search authentication values
|
|
125
|
+
* @param params - Authentication parameters
|
|
126
|
+
* @returns Authentication result
|
|
127
|
+
*/
|
|
128
|
+
export async function loadWebSearchAuth({
|
|
129
|
+
userId,
|
|
130
|
+
webSearchConfig,
|
|
131
|
+
loadAuthValues,
|
|
132
|
+
throwError = true,
|
|
133
|
+
}: {
|
|
134
|
+
userId: string;
|
|
135
|
+
webSearchConfig: TCustomConfig['webSearch'];
|
|
136
|
+
loadAuthValues: (params: {
|
|
137
|
+
userId: string;
|
|
138
|
+
authFields: string[];
|
|
139
|
+
optional?: Set<string>;
|
|
140
|
+
throwError?: boolean;
|
|
141
|
+
}) => Promise<Record<string, string>>;
|
|
142
|
+
throwError?: boolean;
|
|
143
|
+
}): Promise<WebSearchAuthResult> {
|
|
144
|
+
let authenticated = true;
|
|
145
|
+
const authResult: Partial<TWebSearchConfig> = {};
|
|
146
|
+
|
|
147
|
+
/** Type-safe iterator for the category-service combinations */
|
|
148
|
+
async function checkAuth<C extends TWebSearchCategories>(
|
|
149
|
+
category: C,
|
|
150
|
+
): Promise<[boolean, boolean]> {
|
|
151
|
+
type ServiceType = keyof (typeof webSearchAuth)[C];
|
|
152
|
+
let isUserProvided = false;
|
|
153
|
+
|
|
154
|
+
// Check if a specific service is specified in the config
|
|
155
|
+
let specificService: ServiceType | undefined;
|
|
156
|
+
if (category === SearchCategories.PROVIDERS && webSearchConfig?.searchProvider) {
|
|
157
|
+
specificService = webSearchConfig.searchProvider as unknown as ServiceType;
|
|
158
|
+
} else if (category === SearchCategories.SCRAPERS && webSearchConfig?.scraperType) {
|
|
159
|
+
specificService = webSearchConfig.scraperType as unknown as ServiceType;
|
|
160
|
+
} else if (category === SearchCategories.RERANKERS && webSearchConfig?.rerankerType) {
|
|
161
|
+
specificService = webSearchConfig.rerankerType as unknown as ServiceType;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// If a specific service is specified, only check that one
|
|
165
|
+
const services = specificService
|
|
166
|
+
? [specificService]
|
|
167
|
+
: (Object.keys(webSearchAuth[category]) as ServiceType[]);
|
|
168
|
+
|
|
169
|
+
for (const service of services) {
|
|
170
|
+
// Skip if the service doesn't exist in the webSearchAuth config
|
|
171
|
+
if (!webSearchAuth[category][service]) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const serviceConfig = webSearchAuth[category][service];
|
|
176
|
+
|
|
177
|
+
// Split keys into required and optional
|
|
178
|
+
const requiredKeys: TWebSearchKeys[] = [];
|
|
179
|
+
const optionalKeys: TWebSearchKeys[] = [];
|
|
180
|
+
|
|
181
|
+
for (const key in serviceConfig) {
|
|
182
|
+
const typedKey = key as TWebSearchKeys;
|
|
183
|
+
if (serviceConfig[typedKey as keyof typeof serviceConfig] === 1) {
|
|
184
|
+
requiredKeys.push(typedKey);
|
|
185
|
+
} else if (serviceConfig[typedKey as keyof typeof serviceConfig] === 0) {
|
|
186
|
+
optionalKeys.push(typedKey);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (requiredKeys.length === 0) continue;
|
|
191
|
+
|
|
192
|
+
const requiredAuthFields = extractWebSearchEnvVars({
|
|
193
|
+
keys: requiredKeys,
|
|
194
|
+
config: webSearchConfig,
|
|
195
|
+
});
|
|
196
|
+
const optionalAuthFields = extractWebSearchEnvVars({
|
|
197
|
+
keys: optionalKeys,
|
|
198
|
+
config: webSearchConfig,
|
|
199
|
+
});
|
|
200
|
+
if (requiredAuthFields.length !== requiredKeys.length) continue;
|
|
201
|
+
|
|
202
|
+
const allKeys = [...requiredKeys, ...optionalKeys];
|
|
203
|
+
const allAuthFields = [...requiredAuthFields, ...optionalAuthFields];
|
|
204
|
+
const optionalSet = new Set(optionalAuthFields);
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const authValues = await loadAuthValues({
|
|
208
|
+
userId,
|
|
209
|
+
authFields: allAuthFields,
|
|
210
|
+
optional: optionalSet,
|
|
211
|
+
throwError,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
let allFieldsAuthenticated = true;
|
|
215
|
+
for (let j = 0; j < allAuthFields.length; j++) {
|
|
216
|
+
const field = allAuthFields[j];
|
|
217
|
+
const value = authValues[field];
|
|
218
|
+
const originalKey = allKeys[j];
|
|
219
|
+
if (originalKey) authResult[originalKey] = value;
|
|
220
|
+
if (!optionalSet.has(field) && !value) {
|
|
221
|
+
allFieldsAuthenticated = false;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
if (!isUserProvided && process.env[field] !== value) {
|
|
225
|
+
isUserProvided = true;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!allFieldsAuthenticated) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (category === SearchCategories.PROVIDERS) {
|
|
233
|
+
authResult.searchProvider = service as SearchProviders;
|
|
234
|
+
} else if (category === SearchCategories.SCRAPERS) {
|
|
235
|
+
authResult.scraperType = service as ScraperTypes;
|
|
236
|
+
} else if (category === SearchCategories.RERANKERS) {
|
|
237
|
+
authResult.rerankerType = service as RerankerTypes;
|
|
238
|
+
}
|
|
239
|
+
return [true, isUserProvided];
|
|
240
|
+
} catch {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return [false, isUserProvided];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const categories = [
|
|
248
|
+
SearchCategories.PROVIDERS,
|
|
249
|
+
SearchCategories.SCRAPERS,
|
|
250
|
+
SearchCategories.RERANKERS,
|
|
251
|
+
] as const;
|
|
252
|
+
const authTypes: [TWebSearchCategories, AuthType][] = [];
|
|
253
|
+
for (const category of categories) {
|
|
254
|
+
const [isCategoryAuthenticated, isUserProvided] = await checkAuth(category);
|
|
255
|
+
if (!isCategoryAuthenticated) {
|
|
256
|
+
authenticated = false;
|
|
257
|
+
authTypes.push([category, AuthType.USER_PROVIDED]);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
authTypes.push([category, isUserProvided ? AuthType.USER_PROVIDED : AuthType.SYSTEM_DEFINED]);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
authResult.safeSearch = webSearchConfig?.safeSearch ?? SafeSearchTypes.MODERATE;
|
|
264
|
+
authResult.scraperTimeout = webSearchConfig?.scraperTimeout ?? 7500;
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
authTypes,
|
|
268
|
+
authResult,
|
|
269
|
+
authenticated,
|
|
270
|
+
};
|
|
271
|
+
}
|