@librechat/agents 2.4.318 → 2.4.320
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/cjs/events.cjs +3 -3
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/main.cjs +5 -2
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/ids.cjs +23 -0
- package/dist/cjs/messages/ids.cjs.map +1 -0
- package/dist/cjs/stream.cjs +8 -155
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +144 -0
- package/dist/cjs/tools/handlers.cjs.map +1 -0
- package/dist/cjs/tools/search/search.cjs +18 -8
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +85 -61
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs +3 -1
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/esm/events.mjs +1 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/ids.mjs +21 -0
- package/dist/esm/messages/ids.mjs.map +1 -0
- package/dist/esm/stream.mjs +7 -152
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +141 -0
- package/dist/esm/tools/handlers.mjs.map +1 -0
- package/dist/esm/tools/search/search.mjs +18 -8
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +84 -60
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs +3 -1
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/messages/ids.d.ts +3 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/stream.d.ts +0 -8
- package/dist/types/tools/handlers.d.ts +8 -0
- package/dist/types/tools/search/search.d.ts +1 -1
- package/dist/types/tools/search/types.d.ts +28 -8
- package/package.json +1 -1
- package/src/events.ts +49 -15
- package/src/index.ts +1 -0
- package/src/messages/ids.ts +26 -0
- package/src/messages/index.ts +1 -0
- package/src/stream.ts +4 -186
- package/src/tools/handlers.ts +167 -0
- package/src/tools/search/search.ts +27 -15
- package/src/tools/search/tool.ts +137 -91
- package/src/tools/search/types.ts +37 -10
- package/src/tools/search/utils.ts +5 -1
package/src/tools/search/tool.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
4
|
+
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
4
5
|
import type * as t from './types';
|
|
5
6
|
import { createSearchAPI, createSourceProcessor } from './search';
|
|
6
7
|
import { createFirecrawlScraper } from './firecrawl';
|
|
@@ -38,6 +39,129 @@ Examples:
|
|
|
38
39
|
- "in" for India
|
|
39
40
|
`.trim();
|
|
40
41
|
|
|
42
|
+
function createSearchProcessor({
|
|
43
|
+
searchAPI,
|
|
44
|
+
sourceProcessor,
|
|
45
|
+
onGetHighlights,
|
|
46
|
+
}: {
|
|
47
|
+
searchAPI: ReturnType<typeof createSearchAPI>;
|
|
48
|
+
sourceProcessor: ReturnType<typeof createSourceProcessor>;
|
|
49
|
+
onGetHighlights: t.SearchToolConfig['onGetHighlights'];
|
|
50
|
+
}) {
|
|
51
|
+
return async function ({
|
|
52
|
+
query,
|
|
53
|
+
country,
|
|
54
|
+
proMode = true,
|
|
55
|
+
maxSources = 5,
|
|
56
|
+
onSearchResults,
|
|
57
|
+
}: {
|
|
58
|
+
query: string;
|
|
59
|
+
country?: string;
|
|
60
|
+
maxSources?: number;
|
|
61
|
+
proMode?: boolean;
|
|
62
|
+
onSearchResults: t.SearchToolConfig['onSearchResults'];
|
|
63
|
+
}): Promise<t.SearchResultData> {
|
|
64
|
+
try {
|
|
65
|
+
const result = await searchAPI.getSources({ query, country });
|
|
66
|
+
onSearchResults?.(result);
|
|
67
|
+
|
|
68
|
+
if (!result.success) {
|
|
69
|
+
throw new Error(result.error ?? 'Search failed');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const processedSources = await sourceProcessor.processSources({
|
|
73
|
+
query,
|
|
74
|
+
result,
|
|
75
|
+
proMode,
|
|
76
|
+
onGetHighlights,
|
|
77
|
+
numElements: maxSources,
|
|
78
|
+
});
|
|
79
|
+
return expandHighlights(processedSources);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('Error in search:', error);
|
|
82
|
+
return {
|
|
83
|
+
organic: [],
|
|
84
|
+
topStories: [],
|
|
85
|
+
images: [],
|
|
86
|
+
relatedSearches: [],
|
|
87
|
+
error: error instanceof Error ? error.message : String(error),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function createOnSearchResults({
|
|
94
|
+
runnableConfig,
|
|
95
|
+
onSearchResults,
|
|
96
|
+
}: {
|
|
97
|
+
runnableConfig: RunnableConfig;
|
|
98
|
+
onSearchResults: t.SearchToolConfig['onSearchResults'];
|
|
99
|
+
}) {
|
|
100
|
+
return function (results: t.SearchResult): void {
|
|
101
|
+
if (!onSearchResults) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
onSearchResults(results, runnableConfig);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function createTool({
|
|
109
|
+
schema,
|
|
110
|
+
search,
|
|
111
|
+
onSearchResults: _onSearchResults,
|
|
112
|
+
}: {
|
|
113
|
+
schema: t.SearchToolSchema;
|
|
114
|
+
search: ReturnType<typeof createSearchProcessor>;
|
|
115
|
+
onSearchResults: t.SearchToolConfig['onSearchResults'];
|
|
116
|
+
}): DynamicStructuredTool<typeof schema> {
|
|
117
|
+
return tool<typeof schema>(
|
|
118
|
+
async (params, runnableConfig) => {
|
|
119
|
+
const { query, country: _c } = params;
|
|
120
|
+
const country = typeof _c === 'string' && _c ? _c : undefined;
|
|
121
|
+
const searchResult = await search({
|
|
122
|
+
query,
|
|
123
|
+
country,
|
|
124
|
+
onSearchResults: createOnSearchResults({
|
|
125
|
+
runnableConfig,
|
|
126
|
+
onSearchResults: _onSearchResults,
|
|
127
|
+
}),
|
|
128
|
+
});
|
|
129
|
+
const turn = runnableConfig.toolCall?.turn ?? 0;
|
|
130
|
+
const { output, references } = formatResultsForLLM(turn, searchResult);
|
|
131
|
+
const data: t.SearchResultData = { turn, ...searchResult, references };
|
|
132
|
+
return [output, { [Constants.WEB_SEARCH]: data }];
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: Constants.WEB_SEARCH,
|
|
136
|
+
description: `Real-time search. Results have required citation anchors.
|
|
137
|
+
|
|
138
|
+
Note: Use ONCE per reply unless instructed otherwise.
|
|
139
|
+
|
|
140
|
+
Anchors:
|
|
141
|
+
- \\ue202turnXtypeY
|
|
142
|
+
- X = turn idx, type = 'search' | 'news' | 'image' | 'ref', Y = item idx
|
|
143
|
+
|
|
144
|
+
Special Markers:
|
|
145
|
+
- \\ue203...\\ue204 — highlight start/end of cited text (for Standalone or Group citations)
|
|
146
|
+
- \\ue200...\\ue201 — group block (e.g. \\ue200\\ue202turn0search1\\ue202turn0news2\\ue201)
|
|
147
|
+
|
|
148
|
+
**CITE EVERY NON-OBVIOUS FACT/QUOTE:**
|
|
149
|
+
Use anchor marker(s) immediately after the statement:
|
|
150
|
+
- Standalone: "Pure functions produce same output. \\ue202turn0search0"
|
|
151
|
+
- Standalone (multiple): "Today's News \\ue202turn0search0\\ue202turn0news0"
|
|
152
|
+
- Highlight: "\\ue203Highlight text.\\ue204\\ue202turn0news1"
|
|
153
|
+
- Group: "Sources. \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
154
|
+
- Group Highlight: "\\ue203Highlight for group.\\ue204 \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
155
|
+
- Image: "See photo \\ue202turn0image0."
|
|
156
|
+
|
|
157
|
+
**NEVER use markdown links, [1], or footnotes. CITE ONLY with anchors provided.**
|
|
158
|
+
`.trim(),
|
|
159
|
+
schema: schema,
|
|
160
|
+
responseFormat: Constants.CONTENT_AND_ARTIFACT,
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
41
165
|
/**
|
|
42
166
|
* Creates a search tool with a schema that dynamically includes the country field
|
|
43
167
|
* only when the searchProvider is 'serper'.
|
|
@@ -47,7 +171,7 @@ Examples:
|
|
|
47
171
|
*/
|
|
48
172
|
export const createSearchTool = (
|
|
49
173
|
config: t.SearchToolConfig = {}
|
|
50
|
-
): DynamicStructuredTool<typeof
|
|
174
|
+
): DynamicStructuredTool<typeof toolSchema> => {
|
|
51
175
|
const {
|
|
52
176
|
searchProvider = 'serper',
|
|
53
177
|
serperApiKey,
|
|
@@ -63,6 +187,7 @@ export const createSearchTool = (
|
|
|
63
187
|
jinaApiKey,
|
|
64
188
|
cohereApiKey,
|
|
65
189
|
onSearchResults: _onSearchResults,
|
|
190
|
+
onGetHighlights,
|
|
66
191
|
} = config;
|
|
67
192
|
|
|
68
193
|
const querySchema = z.string().describe(DEFAULT_QUERY_DESCRIPTION);
|
|
@@ -80,7 +205,7 @@ export const createSearchTool = (
|
|
|
80
205
|
.describe(DEFAULT_COUNTRY_DESCRIPTION);
|
|
81
206
|
}
|
|
82
207
|
|
|
83
|
-
const
|
|
208
|
+
const toolSchema = z.object(schemaObject);
|
|
84
209
|
|
|
85
210
|
const searchAPI = createSearchAPI({
|
|
86
211
|
searchProvider,
|
|
@@ -115,94 +240,15 @@ export const createSearchTool = (
|
|
|
115
240
|
firecrawlScraper
|
|
116
241
|
);
|
|
117
242
|
|
|
118
|
-
const search =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
onSearchResults,
|
|
124
|
-
}: {
|
|
125
|
-
query: string;
|
|
126
|
-
country?: string;
|
|
127
|
-
maxSources?: number;
|
|
128
|
-
proMode?: boolean;
|
|
129
|
-
onSearchResults?: (sources: t.SearchResult) => void;
|
|
130
|
-
}): Promise<t.SearchResultData> => {
|
|
131
|
-
try {
|
|
132
|
-
const sources = await searchAPI.getSources({ query, country });
|
|
133
|
-
onSearchResults?.(sources);
|
|
134
|
-
|
|
135
|
-
if (!sources.success) {
|
|
136
|
-
throw new Error(sources.error ?? 'Search failed');
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const processedSources = await sourceProcessor.processSources(
|
|
140
|
-
sources,
|
|
141
|
-
maxSources,
|
|
142
|
-
query,
|
|
143
|
-
proMode
|
|
144
|
-
);
|
|
145
|
-
return expandHighlights(processedSources);
|
|
146
|
-
} catch (error) {
|
|
147
|
-
console.error('Error in search:', error);
|
|
148
|
-
return {
|
|
149
|
-
organic: [],
|
|
150
|
-
topStories: [],
|
|
151
|
-
images: [],
|
|
152
|
-
relatedSearches: [],
|
|
153
|
-
error: error instanceof Error ? error.message : String(error),
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
return tool<typeof SearchToolSchema>(
|
|
159
|
-
async (params, runnableConfig) => {
|
|
160
|
-
const { query, country: _c } = params;
|
|
161
|
-
const country = typeof _c === 'string' && _c ? _c : undefined;
|
|
162
|
-
const searchResult = await search({
|
|
163
|
-
query,
|
|
164
|
-
country,
|
|
165
|
-
onSearchResults: _onSearchResults
|
|
166
|
-
? (result): void => {
|
|
167
|
-
_onSearchResults(result, runnableConfig);
|
|
168
|
-
}
|
|
169
|
-
: undefined,
|
|
170
|
-
});
|
|
171
|
-
const turn = runnableConfig.toolCall?.turn ?? 0;
|
|
172
|
-
const { output, references } = formatResultsForLLM(turn, searchResult);
|
|
173
|
-
return [
|
|
174
|
-
output,
|
|
175
|
-
{ [Constants.WEB_SEARCH]: { turn, ...searchResult, references } },
|
|
176
|
-
];
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
name: Constants.WEB_SEARCH,
|
|
180
|
-
description: `
|
|
181
|
-
Real-time search. Results have required citation anchors.
|
|
182
|
-
|
|
183
|
-
Note: Use ONCE per reply unless instructed otherwise.
|
|
184
|
-
|
|
185
|
-
Anchors:
|
|
186
|
-
- \\ue202turnXtypeY
|
|
187
|
-
- X = turn idx, type = 'search' | 'news' | 'image' | 'ref', Y = item idx
|
|
188
|
-
|
|
189
|
-
Special Markers:
|
|
190
|
-
- \\ue203...\\ue204 — highlight start/end of cited text (for Standalone or Group citations)
|
|
191
|
-
- \\ue200...\\ue201 — group block (e.g. \\ue200\\ue202turn0search1\\ue202turn0news2\\ue201)
|
|
192
|
-
|
|
193
|
-
**CITE EVERY NON-OBVIOUS FACT/QUOTE:**
|
|
194
|
-
Use anchor marker(s) immediately after the statement:
|
|
195
|
-
- Standalone: "Pure functions produce same output. \\ue202turn0search0"
|
|
196
|
-
- Standalone (multiple): "Today's News \\ue202turn0search0\\ue202turn0news0"
|
|
197
|
-
- Highlight: "\\ue203Highlight text.\\ue204\\ue202turn0news1"
|
|
198
|
-
- Group: "Sources. \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
199
|
-
- Group Highlight: "\\ue203Highlight for group.\\ue204 \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
200
|
-
- Image: "See photo \\ue202turn0image0."
|
|
243
|
+
const search = createSearchProcessor({
|
|
244
|
+
searchAPI,
|
|
245
|
+
sourceProcessor,
|
|
246
|
+
onGetHighlights,
|
|
247
|
+
});
|
|
201
248
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
);
|
|
249
|
+
return createTool({
|
|
250
|
+
search,
|
|
251
|
+
schema: toolSchema,
|
|
252
|
+
onSearchResults: _onSearchResults,
|
|
253
|
+
});
|
|
208
254
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
2
3
|
import type { BaseReranker } from './rerankers';
|
|
3
4
|
|
|
@@ -27,6 +28,7 @@ export type ResultReference = {
|
|
|
27
28
|
attribution?: string;
|
|
28
29
|
};
|
|
29
30
|
export interface SearchResultData {
|
|
31
|
+
turn?: number;
|
|
30
32
|
organic?: ProcessedOrganic[];
|
|
31
33
|
topStories?: ProcessedTopStory[];
|
|
32
34
|
images?: ImageResult[];
|
|
@@ -98,15 +100,6 @@ export interface ScraperExtractionResult {
|
|
|
98
100
|
no_extraction: ScraperContentResult;
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
// Define type for SearXNG result
|
|
102
|
-
export interface SearXNGResult {
|
|
103
|
-
title?: string;
|
|
104
|
-
url?: string;
|
|
105
|
-
content?: string;
|
|
106
|
-
publishedDate?: string;
|
|
107
|
-
img_src?: string;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
103
|
export interface JinaRerankerResult {
|
|
111
104
|
index: number;
|
|
112
105
|
relevance_score: number;
|
|
@@ -151,6 +144,7 @@ export interface SearchToolConfig
|
|
|
151
144
|
results: SearchResult,
|
|
152
145
|
runnableConfig?: RunnableConfig
|
|
153
146
|
) => void;
|
|
147
|
+
onGetHighlights?: (link: string) => void;
|
|
154
148
|
}
|
|
155
149
|
export interface MediaReference {
|
|
156
150
|
originalUrl: string;
|
|
@@ -213,7 +207,7 @@ export interface ScrapeMetadata {
|
|
|
213
207
|
publishedTime?: string;
|
|
214
208
|
modifiedTime?: string;
|
|
215
209
|
// Twitter metadata
|
|
216
|
-
'twitter:site'?: string;
|
|
210
|
+
'twitter:site'?: string | boolean | number | null;
|
|
217
211
|
'twitter:creator'?: string;
|
|
218
212
|
'twitter:card'?: string;
|
|
219
213
|
'twitter:image'?: string;
|
|
@@ -555,3 +549,36 @@ export interface SearxNGSearchPayload {
|
|
|
555
549
|
*/
|
|
556
550
|
disabled_engines?: string;
|
|
557
551
|
}
|
|
552
|
+
|
|
553
|
+
export interface SearXNGResult {
|
|
554
|
+
title?: string;
|
|
555
|
+
url?: string;
|
|
556
|
+
content?: string;
|
|
557
|
+
publishedDate?: string;
|
|
558
|
+
img_src?: string;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export type ProcessSourcesFields = {
|
|
562
|
+
result: SearchResult;
|
|
563
|
+
numElements: number;
|
|
564
|
+
query: string;
|
|
565
|
+
proMode: boolean;
|
|
566
|
+
onGetHighlights: SearchToolConfig['onGetHighlights'];
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
export type SearchToolSchema = z.ZodObject<
|
|
570
|
+
{
|
|
571
|
+
query: z.ZodString;
|
|
572
|
+
country?: z.ZodOptional<z.ZodString>;
|
|
573
|
+
},
|
|
574
|
+
'strip',
|
|
575
|
+
z.ZodTypeAny,
|
|
576
|
+
{
|
|
577
|
+
query: string;
|
|
578
|
+
country?: unknown;
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
query: string;
|
|
582
|
+
country?: unknown;
|
|
583
|
+
}
|
|
584
|
+
>;
|
|
@@ -25,11 +25,15 @@ export function getAttribution(
|
|
|
25
25
|
): string | undefined {
|
|
26
26
|
if (!metadata) return getDomainName(link, metadata);
|
|
27
27
|
|
|
28
|
+
const twitterSite = metadata['twitter:site'];
|
|
29
|
+
const twitterSiteFormatted =
|
|
30
|
+
typeof twitterSite === 'string' ? twitterSite.replace(/^@/, '') : undefined;
|
|
31
|
+
|
|
28
32
|
const possibleAttributions = [
|
|
29
33
|
metadata.ogSiteName,
|
|
30
34
|
metadata['og:site_name'],
|
|
31
35
|
metadata.title?.split('|').pop()?.trim(),
|
|
32
|
-
|
|
36
|
+
twitterSiteFormatted,
|
|
33
37
|
];
|
|
34
38
|
|
|
35
39
|
const attribution = possibleAttributions.find(
|