@librechat/agents 2.4.319 → 2.4.321
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/tools/search/format.cjs +111 -80
- package/dist/cjs/tools/search/format.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +83 -37
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +83 -57
- 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/tools/search/format.mjs +111 -80
- package/dist/esm/tools/search/format.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +83 -37
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +82 -56
- 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/tools/search/search.d.ts +1 -1
- package/dist/types/tools/search/types.d.ts +22 -1
- package/package.json +1 -1
- package/src/scripts/search.ts +4 -1
- package/src/tools/search/format.ts +149 -86
- package/src/tools/search/search.ts +120 -47
- package/src/tools/search/tool.ts +137 -89
- package/src/tools/search/types.ts +30 -1
- 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,92 +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
|
-
const data: t.SearchResultData = { turn, ...searchResult, references };
|
|
174
|
-
return [output, { [Constants.WEB_SEARCH]: data }];
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
name: Constants.WEB_SEARCH,
|
|
178
|
-
description: `
|
|
179
|
-
Real-time search. Results have required citation anchors.
|
|
180
|
-
|
|
181
|
-
Note: Use ONCE per reply unless instructed otherwise.
|
|
182
|
-
|
|
183
|
-
Anchors:
|
|
184
|
-
- \\ue202turnXtypeY
|
|
185
|
-
- X = turn idx, type = 'search' | 'news' | 'image' | 'ref', Y = item idx
|
|
186
|
-
|
|
187
|
-
Special Markers:
|
|
188
|
-
- \\ue203...\\ue204 — highlight start/end of cited text (for Standalone or Group citations)
|
|
189
|
-
- \\ue200...\\ue201 — group block (e.g. \\ue200\\ue202turn0search1\\ue202turn0news2\\ue201)
|
|
190
|
-
|
|
191
|
-
**CITE EVERY NON-OBVIOUS FACT/QUOTE:**
|
|
192
|
-
Use anchor marker(s) immediately after the statement:
|
|
193
|
-
- Standalone: "Pure functions produce same output. \\ue202turn0search0"
|
|
194
|
-
- Standalone (multiple): "Today's News \\ue202turn0search0\\ue202turn0news0"
|
|
195
|
-
- Highlight: "\\ue203Highlight text.\\ue204\\ue202turn0news1"
|
|
196
|
-
- Group: "Sources. \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
197
|
-
- Group Highlight: "\\ue203Highlight for group.\\ue204 \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
198
|
-
- Image: "See photo \\ue202turn0image0."
|
|
243
|
+
const search = createSearchProcessor({
|
|
244
|
+
searchAPI,
|
|
245
|
+
sourceProcessor,
|
|
246
|
+
onGetHighlights,
|
|
247
|
+
});
|
|
199
248
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
);
|
|
249
|
+
return createTool({
|
|
250
|
+
search,
|
|
251
|
+
schema: toolSchema,
|
|
252
|
+
onSearchResults: _onSearchResults,
|
|
253
|
+
});
|
|
206
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
|
|
|
@@ -15,6 +16,7 @@ export type ProcessedSource = {
|
|
|
15
16
|
attribution?: string;
|
|
16
17
|
references?: References;
|
|
17
18
|
highlights?: Highlight[];
|
|
19
|
+
processed?: boolean;
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export type ProcessedOrganic = OrganicResult & ProcessedSource;
|
|
@@ -23,6 +25,7 @@ export type ValidSource = ProcessedOrganic | ProcessedTopStory;
|
|
|
23
25
|
|
|
24
26
|
export type ResultReference = {
|
|
25
27
|
link: string;
|
|
28
|
+
type: 'link' | 'image' | 'video';
|
|
26
29
|
title?: string;
|
|
27
30
|
attribution?: string;
|
|
28
31
|
};
|
|
@@ -143,6 +146,7 @@ export interface SearchToolConfig
|
|
|
143
146
|
results: SearchResult,
|
|
144
147
|
runnableConfig?: RunnableConfig
|
|
145
148
|
) => void;
|
|
149
|
+
onGetHighlights?: (link: string) => void;
|
|
146
150
|
}
|
|
147
151
|
export interface MediaReference {
|
|
148
152
|
originalUrl: string;
|
|
@@ -205,7 +209,7 @@ export interface ScrapeMetadata {
|
|
|
205
209
|
publishedTime?: string;
|
|
206
210
|
modifiedTime?: string;
|
|
207
211
|
// Twitter metadata
|
|
208
|
-
'twitter:site'?: string;
|
|
212
|
+
'twitter:site'?: string | boolean | number | null;
|
|
209
213
|
'twitter:creator'?: string;
|
|
210
214
|
'twitter:card'?: string;
|
|
211
215
|
'twitter:image'?: string;
|
|
@@ -555,3 +559,28 @@ export interface SearXNGResult {
|
|
|
555
559
|
publishedDate?: string;
|
|
556
560
|
img_src?: string;
|
|
557
561
|
}
|
|
562
|
+
|
|
563
|
+
export type ProcessSourcesFields = {
|
|
564
|
+
result: SearchResult;
|
|
565
|
+
numElements: number;
|
|
566
|
+
query: string;
|
|
567
|
+
proMode: boolean;
|
|
568
|
+
onGetHighlights: SearchToolConfig['onGetHighlights'];
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
export type SearchToolSchema = z.ZodObject<
|
|
572
|
+
{
|
|
573
|
+
query: z.ZodString;
|
|
574
|
+
country?: z.ZodOptional<z.ZodString>;
|
|
575
|
+
},
|
|
576
|
+
'strip',
|
|
577
|
+
z.ZodTypeAny,
|
|
578
|
+
{
|
|
579
|
+
query: string;
|
|
580
|
+
country?: unknown;
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
query: string;
|
|
584
|
+
country?: unknown;
|
|
585
|
+
}
|
|
586
|
+
>;
|
|
@@ -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(
|