@librechat/agents 2.4.314 → 2.4.316
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/ToolNode.cjs +14 -3
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/search/format.cjs +3 -3
- package/dist/cjs/tools/search/format.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +32 -14
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +14 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/search/format.mjs +3 -3
- package/dist/esm/tools/search/format.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +32 -14
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/types/tools/ToolNode.d.ts +6 -0
- package/dist/types/tools/example.d.ts +23 -3
- package/dist/types/tools/search/format.d.ts +1 -1
- package/package.json +6 -6
- package/src/tools/ToolNode.ts +37 -14
- package/src/tools/search/format.ts +6 -3
- package/src/tools/search/tool.ts +34 -16
|
@@ -14,14 +14,23 @@ class ToolNode extends run.RunnableCallable {
|
|
|
14
14
|
handleToolErrors = true;
|
|
15
15
|
toolCallStepIds;
|
|
16
16
|
errorHandler;
|
|
17
|
+
toolUsageCount;
|
|
17
18
|
constructor({ tools, toolMap, name, tags, errorHandler, toolCallStepIds, handleToolErrors, loadRuntimeTools, }) {
|
|
18
19
|
super({ name, tags, func: (input, config) => this.run(input, config) });
|
|
19
20
|
this.tools = tools;
|
|
20
|
-
this.toolMap = toolMap ?? new Map(tools.map(tool => [tool.name, tool]));
|
|
21
|
+
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
21
22
|
this.toolCallStepIds = toolCallStepIds;
|
|
22
23
|
this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;
|
|
23
24
|
this.loadRuntimeTools = loadRuntimeTools;
|
|
24
25
|
this.errorHandler = errorHandler;
|
|
26
|
+
this.toolUsageCount = new Map();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns a snapshot of the current tool usage counts.
|
|
30
|
+
* @returns A ReadonlyMap where keys are tool names and values are their usage counts.
|
|
31
|
+
*/
|
|
32
|
+
getToolUsageCounts() {
|
|
33
|
+
return new Map(this.toolUsageCount); // Return a copy
|
|
25
34
|
}
|
|
26
35
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
36
|
async run(input, config) {
|
|
@@ -34,7 +43,7 @@ class ToolNode extends run.RunnableCallable {
|
|
|
34
43
|
if (this.loadRuntimeTools) {
|
|
35
44
|
const { tools, toolMap } = this.loadRuntimeTools(message.tool_calls ?? []);
|
|
36
45
|
this.tools = tools;
|
|
37
|
-
this.toolMap = toolMap ?? new Map(tools.map(tool => [tool.name, tool]));
|
|
46
|
+
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
38
47
|
}
|
|
39
48
|
const outputs = await Promise.all(message.tool_calls?.map(async (call) => {
|
|
40
49
|
const tool = this.toolMap.get(call.name);
|
|
@@ -42,9 +51,11 @@ class ToolNode extends run.RunnableCallable {
|
|
|
42
51
|
if (tool === undefined) {
|
|
43
52
|
throw new Error(`Tool "${call.name}" not found.`);
|
|
44
53
|
}
|
|
54
|
+
const turn = this.toolUsageCount.get(call.name) ?? 0;
|
|
55
|
+
this.toolUsageCount.set(call.name, turn + 1);
|
|
45
56
|
const args = call.args;
|
|
46
57
|
const stepId = this.toolCallStepIds?.get(call.id);
|
|
47
|
-
const output = await tool.invoke({ ...call, args, type: 'tool_call', stepId }, config);
|
|
58
|
+
const output = await tool.invoke({ ...call, args, type: 'tool_call', stepId, turn }, config);
|
|
48
59
|
if ((messages.isBaseMessage(output) && output._getType() === 'tool') ||
|
|
49
60
|
langgraph.isCommand(output)) {
|
|
50
61
|
return output;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolNode.cjs","sources":["../../../src/tools/ToolNode.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"ToolNode.cjs","sources":["../../../src/tools/ToolNode.ts"],"sourcesContent":["import {\n END,\n MessagesAnnotation,\n isCommand,\n isGraphInterrupt,\n} from '@langchain/langgraph';\nimport { ToolMessage, isBaseMessage } from '@langchain/core/messages';\nimport type {\n RunnableConfig,\n RunnableToolLike,\n} from '@langchain/core/runnables';\nimport type { BaseMessage, AIMessage } from '@langchain/core/messages';\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { RunnableCallable } from '@/utils';\nimport { GraphNodeKeys } from '@/common';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class ToolNode<T = any> extends RunnableCallable<T, T> {\n tools: t.GenericTool[];\n private toolMap: Map<string, StructuredToolInterface | RunnableToolLike>;\n private loadRuntimeTools?: t.ToolRefGenerator;\n handleToolErrors = true;\n toolCallStepIds?: Map<string, string>;\n errorHandler?: t.ToolNodeConstructorParams['errorHandler'];\n private toolUsageCount: Map<string, number>;\n\n constructor({\n tools,\n toolMap,\n name,\n tags,\n errorHandler,\n toolCallStepIds,\n handleToolErrors,\n loadRuntimeTools,\n }: t.ToolNodeConstructorParams) {\n super({ name, tags, func: (input, config) => this.run(input, config) });\n this.tools = tools;\n this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));\n this.toolCallStepIds = toolCallStepIds;\n this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;\n this.loadRuntimeTools = loadRuntimeTools;\n this.errorHandler = errorHandler;\n this.toolUsageCount = new Map<string, number>();\n }\n\n /**\n * Returns a snapshot of the current tool usage counts.\n * @returns A ReadonlyMap where keys are tool names and values are their usage counts.\n */\n public getToolUsageCounts(): ReadonlyMap<string, number> {\n return new Map(this.toolUsageCount); // Return a copy\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n protected async run(input: any, config: RunnableConfig): Promise<T> {\n const message = Array.isArray(input)\n ? input[input.length - 1]\n : input.messages[input.messages.length - 1];\n\n if (message._getType() !== 'ai') {\n throw new Error('ToolNode only accepts AIMessages as input.');\n }\n\n if (this.loadRuntimeTools) {\n const { tools, toolMap } = this.loadRuntimeTools(\n (message as AIMessage).tool_calls ?? []\n );\n this.tools = tools;\n this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));\n }\n const outputs = await Promise.all(\n (message as AIMessage).tool_calls?.map(async (call) => {\n const tool = this.toolMap.get(call.name);\n try {\n if (tool === undefined) {\n throw new Error(`Tool \"${call.name}\" not found.`);\n }\n const turn = this.toolUsageCount.get(call.name) ?? 0;\n this.toolUsageCount.set(call.name, turn + 1);\n const args = call.args;\n const stepId = this.toolCallStepIds?.get(call.id!);\n const output = await tool.invoke(\n { ...call, args, type: 'tool_call', stepId, turn },\n config\n );\n if (\n (isBaseMessage(output) && output._getType() === 'tool') ||\n isCommand(output)\n ) {\n return output;\n } else {\n return new ToolMessage({\n name: tool.name,\n content:\n typeof output === 'string' ? output : JSON.stringify(output),\n tool_call_id: call.id!,\n });\n }\n } catch (_e: unknown) {\n const e = _e as Error;\n if (!this.handleToolErrors) {\n throw e;\n }\n if (isGraphInterrupt(e)) {\n throw e;\n }\n this.errorHandler?.(\n {\n error: e,\n id: call.id!,\n name: call.name,\n input: call.args,\n },\n config.metadata\n );\n return new ToolMessage({\n content: `Error: ${e.message}\\n Please fix your mistakes.`,\n name: call.name,\n tool_call_id: call.id ?? '',\n });\n }\n }) ?? []\n );\n\n if (!outputs.some(isCommand)) {\n return (Array.isArray(input) ? outputs : { messages: outputs }) as T;\n }\n\n const combinedOutputs = outputs.map((output) => {\n if (isCommand(output)) {\n return output;\n }\n return Array.isArray(input) ? [output] : { messages: [output] };\n });\n return combinedOutputs as T;\n }\n}\n\nexport function toolsCondition(\n state: BaseMessage[] | typeof MessagesAnnotation.State\n): 'tools' | typeof END {\n const message = Array.isArray(state)\n ? state[state.length - 1]\n : state.messages[state.messages.length - 1];\n\n if (\n 'tool_calls' in message &&\n ((message as AIMessage).tool_calls?.length ?? 0) > 0\n ) {\n return GraphNodeKeys.TOOLS;\n } else {\n return END;\n }\n}\n"],"names":["RunnableCallable","isBaseMessage","isCommand","ToolMessage","isGraphInterrupt","GraphNodeKeys","END"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
function formatResultsForLLM(results) {
|
|
3
|
+
function formatResultsForLLM(turn, results) {
|
|
4
4
|
let output = '';
|
|
5
5
|
const addSection = (title) => {
|
|
6
6
|
output += `\n=== ${title} ===\n`;
|
|
@@ -8,11 +8,11 @@ function formatResultsForLLM(results) {
|
|
|
8
8
|
// Organic (web) results
|
|
9
9
|
const organic = results.organic ?? [];
|
|
10
10
|
if (organic.length) {
|
|
11
|
-
addSection('Web Results');
|
|
11
|
+
addSection('Web Results, Turn ' + turn);
|
|
12
12
|
organic.forEach((r, i) => {
|
|
13
13
|
output += [
|
|
14
14
|
`Source ${i}: ${r.title ?? '(no title)'}`,
|
|
15
|
-
`Citation Anchor: \\
|
|
15
|
+
`Citation Anchor: \\ue202turn${turn}search${i}`,
|
|
16
16
|
`URL: ${r.link}`,
|
|
17
17
|
r.snippet != null ? `Summary: ${r.snippet}` : '',
|
|
18
18
|
r.date != null ? `Date: ${r.date}` : '',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.cjs","sources":["../../../../src/tools/search/format.ts"],"sourcesContent":["import type * as t from './types';\n\nexport function formatResultsForLLM(results: t.SearchResultData): string {\n let output = '';\n\n const addSection = (title: string): void => {\n output += `\\n=== ${title} ===\\n`;\n };\n\n // Organic (web) results\n const organic = results.organic ?? [];\n if (organic.length) {\n addSection('Web Results');\n organic.forEach((r, i) => {\n output += [\n `Source ${i}: ${r.title ?? '(no title)'}`,\n `Citation Anchor: \\\\
|
|
1
|
+
{"version":3,"file":"format.cjs","sources":["../../../../src/tools/search/format.ts"],"sourcesContent":["import type * as t from './types';\n\nexport function formatResultsForLLM(\n turn: number,\n results: t.SearchResultData\n): string {\n let output = '';\n\n const addSection = (title: string): void => {\n output += `\\n=== ${title} ===\\n`;\n };\n\n // Organic (web) results\n const organic = results.organic ?? [];\n if (organic.length) {\n addSection('Web Results, Turn ' + turn);\n organic.forEach((r, i) => {\n output += [\n `Source ${i}: ${r.title ?? '(no title)'}`,\n `Citation Anchor: \\\\ue202turn${turn}search${i}`,\n `URL: ${r.link}`,\n r.snippet != null ? `Summary: ${r.snippet}` : '',\n r.date != null ? `Date: ${r.date}` : '',\n r.attribution != null ? `Source: ${r.attribution}` : '',\n '',\n '--- Content Highlights ---',\n ...(r.highlights ?? [])\n .filter((h) => h.text.trim().length > 0)\n .map((h) => `[Relevance: ${h.score.toFixed(2)}]\\n${h.text.trim()}`),\n '',\n ]\n .filter(Boolean)\n .join('\\n');\n });\n }\n\n // Ignoring these sections for now\n // // Top stories (news)\n // const topStores = results.topStories ?? [];\n // if (topStores.length) {\n // addSection('News Results');\n // topStores.forEach((r, i) => {\n // output += [\n // `Anchor: \\ue202turn0news${i}`,\n // `Title: ${r.title ?? '(no title)'}`,\n // `URL: ${r.link}`,\n // r.snippet != null ? `Snippet: ${r.snippet}` : '',\n // r.date != null ? `Date: ${r.date}` : '',\n // r.attribution != null ? `Source: ${r.attribution}` : '',\n // ''\n // ].filter(Boolean).join('\\n');\n // });\n // }\n\n // // Images\n // const images = results.images ?? [];\n // if (images.length) {\n // addSection('Image Results');\n // images.forEach((img, i) => {\n // output += [\n // `Anchor: \\ue202turn0image${i}`,\n // `Title: ${img.title ?? '(no title)'}`,\n // `Image URL: ${img.imageUrl}`,\n // ''\n // ].join('\\n');\n // });\n // }\n\n // Knowledge Graph\n if (results.knowledgeGraph != null) {\n addSection('Knowledge Graph');\n output += [\n `Title: ${results.knowledgeGraph.title ?? '(no title)'}`,\n results.knowledgeGraph.description != null\n ? `Description: ${results.knowledgeGraph.description}`\n : '',\n results.knowledgeGraph.type != null\n ? `Type: ${results.knowledgeGraph.type}`\n : '',\n results.knowledgeGraph.imageUrl != null\n ? `Image URL: ${results.knowledgeGraph.imageUrl}`\n : '',\n results.knowledgeGraph.attributes != null\n ? `Attributes: ${JSON.stringify(results.knowledgeGraph.attributes, null, 2)}`\n : '',\n '',\n ]\n .filter(Boolean)\n .join('\\n');\n }\n\n // Answer Box\n if (results.answerBox != null) {\n addSection('Answer Box');\n output += [\n results.answerBox.title != null\n ? `Title: ${results.answerBox.title}`\n : '',\n results.answerBox.answer != null\n ? `Answer: ${results.answerBox.answer}`\n : '',\n results.answerBox.snippet != null\n ? `Snippet: ${results.answerBox.snippet}`\n : '',\n results.answerBox.date != null ? `Date: ${results.answerBox.date}` : '',\n '',\n ]\n .filter(Boolean)\n .join('\\n');\n }\n\n // People also ask\n const peopleAlsoAsk = results.peopleAlsoAsk ?? [];\n if (peopleAlsoAsk.length) {\n addSection('People Also Ask');\n peopleAlsoAsk.forEach((p, _i) => {\n output += [`Q: ${p.question}`, `A: ${p.answer}`, '']\n .filter(Boolean)\n .join('\\n');\n });\n }\n\n return output.trim();\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -11,9 +11,25 @@ var _enum = require('../../common/enum.cjs');
|
|
|
11
11
|
|
|
12
12
|
/* eslint-disable no-console */
|
|
13
13
|
const SearchToolSchema = zod.z.object({
|
|
14
|
-
query: zod.z
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
query: zod.z.string().describe(`
|
|
15
|
+
GUIDELINES:
|
|
16
|
+
- Start broad, then narrow: Begin with key concepts, then refine with specifics
|
|
17
|
+
- Think like sources: Use terminology experts would use in the field
|
|
18
|
+
- Consider perspective: Frame queries from different viewpoints for better results
|
|
19
|
+
- Quality over quantity: A precise 3-4 word query often beats lengthy sentences
|
|
20
|
+
|
|
21
|
+
TECHNIQUES (combine for power searches):
|
|
22
|
+
- EXACT PHRASES: Use quotes ("climate change report")
|
|
23
|
+
- EXCLUDE TERMS: Use minus to remove unwanted results (-wikipedia)
|
|
24
|
+
- SITE-SPECIFIC: Restrict to websites (site:edu research)
|
|
25
|
+
- FILETYPE: Find specific documents (filetype:pdf study)
|
|
26
|
+
- OR OPERATOR: Find alternatives (electric OR hybrid cars)
|
|
27
|
+
- DATE RANGE: Recent information (data after:2020)
|
|
28
|
+
- WILDCARDS: Use * for unknown terms (how to * bread)
|
|
29
|
+
- SPECIFIC QUESTIONS: Use who/what/when/where/why/how
|
|
30
|
+
- DOMAIN TERMS: Include technical terminology for specialized topics
|
|
31
|
+
- CONCISE TERMS: Prioritize keywords over sentences
|
|
32
|
+
`.trim()),
|
|
17
33
|
});
|
|
18
34
|
const createSearchTool = (config = {}) => {
|
|
19
35
|
const { searchProvider = 'serper', serperApiKey, searxngInstanceUrl, searxngApiKey, rerankerType = 'cohere', topResults = 5, strategies = ['no_extraction'], filterContent = true, firecrawlApiKey, firecrawlApiUrl, firecrawlFormats = ['markdown', 'html'], jinaApiKey, cohereApiKey, onSearchResults: _onSearchResults, } = config;
|
|
@@ -69,27 +85,29 @@ const createSearchTool = (config = {}) => {
|
|
|
69
85
|
}
|
|
70
86
|
: undefined,
|
|
71
87
|
});
|
|
72
|
-
const
|
|
73
|
-
|
|
88
|
+
const turn = runnableConfig.toolCall?.turn ?? 0;
|
|
89
|
+
const output = format.formatResultsForLLM(turn, searchResult);
|
|
90
|
+
return [output, { [_enum.Constants.WEB_SEARCH]: { turn, ...searchResult } }];
|
|
74
91
|
}, {
|
|
75
92
|
name: _enum.Constants.WEB_SEARCH,
|
|
76
93
|
description: `
|
|
77
94
|
Real-time search. Results have required unique citation anchors.
|
|
78
95
|
|
|
79
96
|
Anchors:
|
|
80
|
-
- \\
|
|
97
|
+
- \\ue202turnXsearchY (web), \\ue202turnXnewsY (news), \\ue202turnXimageY (image)
|
|
98
|
+
- X = turn, Y = item number
|
|
81
99
|
|
|
82
100
|
Special Markers:
|
|
83
|
-
- \\ue203...\\ue204 —
|
|
84
|
-
- \\ue200...\\ue201 —
|
|
85
|
-
- \\ue206 — marks grouped/summary citation areas
|
|
101
|
+
- \\ue203...\\ue204 — highlight start/end of cited text (for Standalone or Group citations)
|
|
102
|
+
- \\ue200...\\ue201 — group block (e.g. \\ue200\\ue202turn0search1\\ue202turn0news2\\ue201)
|
|
86
103
|
|
|
87
104
|
**CITE EVERY NON-OBVIOUS FACT/QUOTE:**
|
|
88
|
-
|
|
89
|
-
- "Pure functions produce same output \\ue202turn0search0
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
- Group: "
|
|
105
|
+
Use anchor marker(s) immediately after the statement:
|
|
106
|
+
- Standalone: "Pure functions produce same output. \\ue202turn0search0"
|
|
107
|
+
- Standalone (multiple): "Today's News \\ue202turn0search0\\ue202turn0news0"
|
|
108
|
+
- Highlight: "\\ue203Highlight text.\\ue204\\ue202turn0news1"
|
|
109
|
+
- Group: "Sources. \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
110
|
+
- Group Highlight: "\\ue203Highlight for group.\\ue204 \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
93
111
|
- Image: "See photo \\ue202turn0image0."
|
|
94
112
|
|
|
95
113
|
**NEVER use markdown links, [1], or footnotes. CITE ONLY with anchors provided.**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.cjs","sources":["../../../../src/tools/search/tool.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport { z } from 'zod';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from './types';\nimport { createSearchAPI, createSourceProcessor } from './search';\nimport { createFirecrawlScraper } from './firecrawl';\nimport { expandHighlights } from './highlights';\nimport { formatResultsForLLM } from './format';\nimport { createReranker } from './rerankers';\nimport { Constants } from '@/common';\n\nconst SearchToolSchema = z.object({\n query: z
|
|
1
|
+
{"version":3,"file":"tool.cjs","sources":["../../../../src/tools/search/tool.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport { z } from 'zod';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from './types';\nimport { createSearchAPI, createSourceProcessor } from './search';\nimport { createFirecrawlScraper } from './firecrawl';\nimport { expandHighlights } from './highlights';\nimport { formatResultsForLLM } from './format';\nimport { createReranker } from './rerankers';\nimport { Constants } from '@/common';\n\nconst SearchToolSchema = z.object({\n query: z.string().describe(\n `\nGUIDELINES:\n- Start broad, then narrow: Begin with key concepts, then refine with specifics\n- Think like sources: Use terminology experts would use in the field\n- Consider perspective: Frame queries from different viewpoints for better results\n- Quality over quantity: A precise 3-4 word query often beats lengthy sentences\n\nTECHNIQUES (combine for power searches):\n- EXACT PHRASES: Use quotes (\"climate change report\")\n- EXCLUDE TERMS: Use minus to remove unwanted results (-wikipedia)\n- SITE-SPECIFIC: Restrict to websites (site:edu research)\n- FILETYPE: Find specific documents (filetype:pdf study)\n- OR OPERATOR: Find alternatives (electric OR hybrid cars)\n- DATE RANGE: Recent information (data after:2020)\n- WILDCARDS: Use * for unknown terms (how to * bread)\n- SPECIFIC QUESTIONS: Use who/what/when/where/why/how\n- DOMAIN TERMS: Include technical terminology for specialized topics\n- CONCISE TERMS: Prioritize keywords over sentences\n`.trim()\n ),\n});\n\nexport const createSearchTool = (\n config: t.SearchToolConfig = {}\n): DynamicStructuredTool<typeof SearchToolSchema> => {\n const {\n searchProvider = 'serper',\n serperApiKey,\n searxngInstanceUrl,\n searxngApiKey,\n rerankerType = 'cohere',\n topResults = 5,\n strategies = ['no_extraction'],\n filterContent = true,\n firecrawlApiKey,\n firecrawlApiUrl,\n firecrawlFormats = ['markdown', 'html'],\n jinaApiKey,\n cohereApiKey,\n onSearchResults: _onSearchResults,\n } = config;\n\n const searchAPI = createSearchAPI({\n searchProvider,\n serperApiKey,\n searxngInstanceUrl,\n searxngApiKey,\n });\n\n const firecrawlScraper = createFirecrawlScraper({\n apiKey: firecrawlApiKey ?? process.env.FIRECRAWL_API_KEY,\n apiUrl: firecrawlApiUrl,\n formats: firecrawlFormats,\n });\n\n const selectedReranker = createReranker({\n rerankerType,\n jinaApiKey,\n cohereApiKey,\n });\n\n if (!selectedReranker) {\n console.warn('No reranker selected. Using default ranking.');\n }\n\n const sourceProcessor = createSourceProcessor(\n {\n reranker: selectedReranker,\n topResults,\n strategies,\n filterContent,\n },\n firecrawlScraper\n );\n\n const search = async ({\n query,\n proMode = true,\n maxSources = 5,\n onSearchResults,\n }: {\n query: string;\n proMode?: boolean;\n maxSources?: number;\n onSearchResults?: (sources: t.SearchResult) => void;\n }): Promise<t.SearchResultData> => {\n try {\n const sources = await searchAPI.getSources(query);\n onSearchResults?.(sources);\n\n if (!sources.success) {\n throw new Error(sources.error ?? 'Search failed');\n }\n\n const processedSources = await sourceProcessor.processSources(\n sources,\n maxSources,\n query,\n proMode\n );\n return expandHighlights(processedSources);\n } catch (error) {\n console.error('Error in search:', error);\n return {\n organic: [],\n topStories: [],\n images: [],\n relatedSearches: [],\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n\n return tool<typeof SearchToolSchema>(\n async ({ query }, runnableConfig) => {\n const searchResult = await search({\n query,\n onSearchResults: _onSearchResults\n ? (result): void => {\n _onSearchResults(result, runnableConfig);\n }\n : undefined,\n });\n const turn = runnableConfig.toolCall?.turn ?? 0;\n const output = formatResultsForLLM(turn, searchResult);\n return [output, { [Constants.WEB_SEARCH]: { turn, ...searchResult } }];\n },\n {\n name: Constants.WEB_SEARCH,\n description: `\nReal-time search. Results have required unique citation anchors.\n\nAnchors:\n- \\\\ue202turnXsearchY (web), \\\\ue202turnXnewsY (news), \\\\ue202turnXimageY (image)\n- X = turn, Y = item number\n\nSpecial Markers:\n- \\\\ue203...\\\\ue204 — highlight start/end of cited text (for Standalone or Group citations)\n- \\\\ue200...\\\\ue201 — group block (e.g. \\\\ue200\\\\ue202turn0search1\\\\ue202turn0news2\\\\ue201)\n\n**CITE EVERY NON-OBVIOUS FACT/QUOTE:**\nUse anchor marker(s) immediately after the statement:\n- Standalone: \"Pure functions produce same output. \\\\ue202turn0search0\"\n- Standalone (multiple): \"Today's News \\\\ue202turn0search0\\\\ue202turn0news0\"\n- Highlight: \"\\\\ue203Highlight text.\\\\ue204\\\\ue202turn0news1\"\n- Group: \"Sources. \\\\ue200\\\\ue202turn0search0\\\\ue202turn0news1\\\\ue201\"\n- Group Highlight: \"\\\\ue203Highlight for group.\\\\ue204 \\\\ue200\\\\ue202turn0search0\\\\ue202turn0news1\\\\ue201\"\n- Image: \"See photo \\\\ue202turn0image0.\"\n\n**NEVER use markdown links, [1], or footnotes. CITE ONLY with anchors provided.**\n`.trim(),\n schema: SearchToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n};\n"],"names":["z","createSearchAPI","createFirecrawlScraper","createReranker","createSourceProcessor","search","expandHighlights","tool","formatResultsForLLM","Constants"],"mappings":";;;;;;;;;;;AAAA;AAWA,MAAM,gBAAgB,GAAGA,KAAC,CAAC,MAAM,CAAC;AAChC,IAAA,KAAK,EAAEA,KAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CACxB;;;;;;;;;;;;;;;;;;CAkBH,CAAC,IAAI,EAAE,CACL;AACF,CAAA,CAAC;MAEW,gBAAgB,GAAG,CAC9B,MAA6B,GAAA,EAAE,KACmB;IAClD,MAAM,EACJ,cAAc,GAAG,QAAQ,EACzB,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,YAAY,GAAG,QAAQ,EACvB,UAAU,GAAG,CAAC,EACd,UAAU,GAAG,CAAC,eAAe,CAAC,EAC9B,aAAa,GAAG,IAAI,EACpB,eAAe,EACf,eAAe,EACf,gBAAgB,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,EACvC,UAAU,EACV,YAAY,EACZ,eAAe,EAAE,gBAAgB,GAClC,GAAG,MAAM;IAEV,MAAM,SAAS,GAAGC,sBAAe,CAAC;QAChC,cAAc;QACd,YAAY;QACZ,kBAAkB;QAClB,aAAa;AACd,KAAA,CAAC;IAEF,MAAM,gBAAgB,GAAGC,gCAAsB,CAAC;AAC9C,QAAA,MAAM,EAAE,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;AACxD,QAAA,MAAM,EAAE,eAAe;AACvB,QAAA,OAAO,EAAE,gBAAgB;AAC1B,KAAA,CAAC;IAEF,MAAM,gBAAgB,GAAGC,wBAAc,CAAC;QACtC,YAAY;QACZ,UAAU;QACV,YAAY;AACb,KAAA,CAAC;IAEF,IAAI,CAAC,gBAAgB,EAAE;AACrB,QAAA,OAAO,CAAC,IAAI,CAAC,8CAA8C,CAAC;;IAG9D,MAAM,eAAe,GAAGC,4BAAqB,CAC3C;AACE,QAAA,QAAQ,EAAE,gBAAgB;QAC1B,WAGD,EACD,gBAAgB,CACjB;AAED,IAAA,MAAMC,QAAM,GAAG,OAAO,EACpB,KAAK,EACL,OAAO,GAAG,IAAI,EACd,UAAU,GAAG,CAAC,EACd,eAAe,GAMhB,KAAiC;AAChC,QAAA,IAAI;YACF,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC;AACjD,YAAA,eAAe,GAAG,OAAO,CAAC;AAE1B,YAAA,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;gBACpB,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,eAAe,CAAC;;AAGnD,YAAA,MAAM,gBAAgB,GAAG,MAAM,eAAe,CAAC,cAAc,CAC3D,OAAO,EACP,UAAU,EACV,KAAK,EACL,OAAO,CACR;AACD,YAAA,OAAOC,2BAAgB,CAAC,gBAAgB,CAAC;;QACzC,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC;YACxC,OAAO;AACL,gBAAA,OAAO,EAAE,EAAE;AACX,gBAAA,UAAU,EAAE,EAAE;AACd,gBAAA,MAAM,EAAE,EAAE;AACV,gBAAA,eAAe,EAAE,EAAE;AACnB,gBAAA,KAAK,EAAE,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;aAC9D;;AAEL,KAAC;IAED,OAAOC,UAAI,CACT,OAAO,EAAE,KAAK,EAAE,EAAE,cAAc,KAAI;AAClC,QAAA,MAAM,YAAY,GAAG,MAAMF,QAAM,CAAC;YAChC,KAAK;AACL,YAAA,eAAe,EAAE;AACf,kBAAE,CAAC,MAAM,KAAU;AACjB,oBAAA,gBAAgB,CAAC,MAAM,EAAE,cAAc,CAAC;;AAE1C,kBAAE,SAAS;AACd,SAAA,CAAC;QACF,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC;QAC/C,MAAM,MAAM,GAAGG,0BAAmB,CAAC,IAAI,EAAE,YAAY,CAAC;AACtD,QAAA,OAAO,CAAC,MAAM,EAAE,EAAE,CAACC,eAAS,CAAC,UAAU,GAAG,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,EAAE,CAAC;AACxE,KAAC,EACD;QACE,IAAI,EAAEA,eAAS,CAAC,UAAU;AAC1B,QAAA,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;AAqBlB,CAAA,CAAC,IAAI,EAAE;AACF,QAAA,MAAM,EAAE,gBAAgB;QACxB,cAAc,EAAEA,eAAS,CAAC,oBAAoB;AAC/C,KAAA,CACF;AACH;;;;"}
|
|
@@ -12,14 +12,23 @@ class ToolNode extends RunnableCallable {
|
|
|
12
12
|
handleToolErrors = true;
|
|
13
13
|
toolCallStepIds;
|
|
14
14
|
errorHandler;
|
|
15
|
+
toolUsageCount;
|
|
15
16
|
constructor({ tools, toolMap, name, tags, errorHandler, toolCallStepIds, handleToolErrors, loadRuntimeTools, }) {
|
|
16
17
|
super({ name, tags, func: (input, config) => this.run(input, config) });
|
|
17
18
|
this.tools = tools;
|
|
18
|
-
this.toolMap = toolMap ?? new Map(tools.map(tool => [tool.name, tool]));
|
|
19
|
+
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
19
20
|
this.toolCallStepIds = toolCallStepIds;
|
|
20
21
|
this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;
|
|
21
22
|
this.loadRuntimeTools = loadRuntimeTools;
|
|
22
23
|
this.errorHandler = errorHandler;
|
|
24
|
+
this.toolUsageCount = new Map();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns a snapshot of the current tool usage counts.
|
|
28
|
+
* @returns A ReadonlyMap where keys are tool names and values are their usage counts.
|
|
29
|
+
*/
|
|
30
|
+
getToolUsageCounts() {
|
|
31
|
+
return new Map(this.toolUsageCount); // Return a copy
|
|
23
32
|
}
|
|
24
33
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
34
|
async run(input, config) {
|
|
@@ -32,7 +41,7 @@ class ToolNode extends RunnableCallable {
|
|
|
32
41
|
if (this.loadRuntimeTools) {
|
|
33
42
|
const { tools, toolMap } = this.loadRuntimeTools(message.tool_calls ?? []);
|
|
34
43
|
this.tools = tools;
|
|
35
|
-
this.toolMap = toolMap ?? new Map(tools.map(tool => [tool.name, tool]));
|
|
44
|
+
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
36
45
|
}
|
|
37
46
|
const outputs = await Promise.all(message.tool_calls?.map(async (call) => {
|
|
38
47
|
const tool = this.toolMap.get(call.name);
|
|
@@ -40,9 +49,11 @@ class ToolNode extends RunnableCallable {
|
|
|
40
49
|
if (tool === undefined) {
|
|
41
50
|
throw new Error(`Tool "${call.name}" not found.`);
|
|
42
51
|
}
|
|
52
|
+
const turn = this.toolUsageCount.get(call.name) ?? 0;
|
|
53
|
+
this.toolUsageCount.set(call.name, turn + 1);
|
|
43
54
|
const args = call.args;
|
|
44
55
|
const stepId = this.toolCallStepIds?.get(call.id);
|
|
45
|
-
const output = await tool.invoke({ ...call, args, type: 'tool_call', stepId }, config);
|
|
56
|
+
const output = await tool.invoke({ ...call, args, type: 'tool_call', stepId, turn }, config);
|
|
46
57
|
if ((isBaseMessage(output) && output._getType() === 'tool') ||
|
|
47
58
|
isCommand(output)) {
|
|
48
59
|
return output;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolNode.mjs","sources":["../../../src/tools/ToolNode.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"ToolNode.mjs","sources":["../../../src/tools/ToolNode.ts"],"sourcesContent":["import {\n END,\n MessagesAnnotation,\n isCommand,\n isGraphInterrupt,\n} from '@langchain/langgraph';\nimport { ToolMessage, isBaseMessage } from '@langchain/core/messages';\nimport type {\n RunnableConfig,\n RunnableToolLike,\n} from '@langchain/core/runnables';\nimport type { BaseMessage, AIMessage } from '@langchain/core/messages';\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { RunnableCallable } from '@/utils';\nimport { GraphNodeKeys } from '@/common';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class ToolNode<T = any> extends RunnableCallable<T, T> {\n tools: t.GenericTool[];\n private toolMap: Map<string, StructuredToolInterface | RunnableToolLike>;\n private loadRuntimeTools?: t.ToolRefGenerator;\n handleToolErrors = true;\n toolCallStepIds?: Map<string, string>;\n errorHandler?: t.ToolNodeConstructorParams['errorHandler'];\n private toolUsageCount: Map<string, number>;\n\n constructor({\n tools,\n toolMap,\n name,\n tags,\n errorHandler,\n toolCallStepIds,\n handleToolErrors,\n loadRuntimeTools,\n }: t.ToolNodeConstructorParams) {\n super({ name, tags, func: (input, config) => this.run(input, config) });\n this.tools = tools;\n this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));\n this.toolCallStepIds = toolCallStepIds;\n this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;\n this.loadRuntimeTools = loadRuntimeTools;\n this.errorHandler = errorHandler;\n this.toolUsageCount = new Map<string, number>();\n }\n\n /**\n * Returns a snapshot of the current tool usage counts.\n * @returns A ReadonlyMap where keys are tool names and values are their usage counts.\n */\n public getToolUsageCounts(): ReadonlyMap<string, number> {\n return new Map(this.toolUsageCount); // Return a copy\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n protected async run(input: any, config: RunnableConfig): Promise<T> {\n const message = Array.isArray(input)\n ? input[input.length - 1]\n : input.messages[input.messages.length - 1];\n\n if (message._getType() !== 'ai') {\n throw new Error('ToolNode only accepts AIMessages as input.');\n }\n\n if (this.loadRuntimeTools) {\n const { tools, toolMap } = this.loadRuntimeTools(\n (message as AIMessage).tool_calls ?? []\n );\n this.tools = tools;\n this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));\n }\n const outputs = await Promise.all(\n (message as AIMessage).tool_calls?.map(async (call) => {\n const tool = this.toolMap.get(call.name);\n try {\n if (tool === undefined) {\n throw new Error(`Tool \"${call.name}\" not found.`);\n }\n const turn = this.toolUsageCount.get(call.name) ?? 0;\n this.toolUsageCount.set(call.name, turn + 1);\n const args = call.args;\n const stepId = this.toolCallStepIds?.get(call.id!);\n const output = await tool.invoke(\n { ...call, args, type: 'tool_call', stepId, turn },\n config\n );\n if (\n (isBaseMessage(output) && output._getType() === 'tool') ||\n isCommand(output)\n ) {\n return output;\n } else {\n return new ToolMessage({\n name: tool.name,\n content:\n typeof output === 'string' ? output : JSON.stringify(output),\n tool_call_id: call.id!,\n });\n }\n } catch (_e: unknown) {\n const e = _e as Error;\n if (!this.handleToolErrors) {\n throw e;\n }\n if (isGraphInterrupt(e)) {\n throw e;\n }\n this.errorHandler?.(\n {\n error: e,\n id: call.id!,\n name: call.name,\n input: call.args,\n },\n config.metadata\n );\n return new ToolMessage({\n content: `Error: ${e.message}\\n Please fix your mistakes.`,\n name: call.name,\n tool_call_id: call.id ?? '',\n });\n }\n }) ?? []\n );\n\n if (!outputs.some(isCommand)) {\n return (Array.isArray(input) ? outputs : { messages: outputs }) as T;\n }\n\n const combinedOutputs = outputs.map((output) => {\n if (isCommand(output)) {\n return output;\n }\n return Array.isArray(input) ? [output] : { messages: [output] };\n });\n return combinedOutputs as T;\n }\n}\n\nexport function toolsCondition(\n state: BaseMessage[] | typeof MessagesAnnotation.State\n): 'tools' | typeof END {\n const message = Array.isArray(state)\n ? state[state.length - 1]\n : state.messages[state.messages.length - 1];\n\n if (\n 'tool_calls' in message &&\n ((message as AIMessage).tool_calls?.length ?? 0) > 0\n ) {\n return GraphNodeKeys.TOOLS;\n } else {\n return END;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function formatResultsForLLM(results) {
|
|
1
|
+
function formatResultsForLLM(turn, results) {
|
|
2
2
|
let output = '';
|
|
3
3
|
const addSection = (title) => {
|
|
4
4
|
output += `\n=== ${title} ===\n`;
|
|
@@ -6,11 +6,11 @@ function formatResultsForLLM(results) {
|
|
|
6
6
|
// Organic (web) results
|
|
7
7
|
const organic = results.organic ?? [];
|
|
8
8
|
if (organic.length) {
|
|
9
|
-
addSection('Web Results');
|
|
9
|
+
addSection('Web Results, Turn ' + turn);
|
|
10
10
|
organic.forEach((r, i) => {
|
|
11
11
|
output += [
|
|
12
12
|
`Source ${i}: ${r.title ?? '(no title)'}`,
|
|
13
|
-
`Citation Anchor: \\
|
|
13
|
+
`Citation Anchor: \\ue202turn${turn}search${i}`,
|
|
14
14
|
`URL: ${r.link}`,
|
|
15
15
|
r.snippet != null ? `Summary: ${r.snippet}` : '',
|
|
16
16
|
r.date != null ? `Date: ${r.date}` : '',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.mjs","sources":["../../../../src/tools/search/format.ts"],"sourcesContent":["import type * as t from './types';\n\nexport function formatResultsForLLM(results: t.SearchResultData): string {\n let output = '';\n\n const addSection = (title: string): void => {\n output += `\\n=== ${title} ===\\n`;\n };\n\n // Organic (web) results\n const organic = results.organic ?? [];\n if (organic.length) {\n addSection('Web Results');\n organic.forEach((r, i) => {\n output += [\n `Source ${i}: ${r.title ?? '(no title)'}`,\n `Citation Anchor: \\\\
|
|
1
|
+
{"version":3,"file":"format.mjs","sources":["../../../../src/tools/search/format.ts"],"sourcesContent":["import type * as t from './types';\n\nexport function formatResultsForLLM(\n turn: number,\n results: t.SearchResultData\n): string {\n let output = '';\n\n const addSection = (title: string): void => {\n output += `\\n=== ${title} ===\\n`;\n };\n\n // Organic (web) results\n const organic = results.organic ?? [];\n if (organic.length) {\n addSection('Web Results, Turn ' + turn);\n organic.forEach((r, i) => {\n output += [\n `Source ${i}: ${r.title ?? '(no title)'}`,\n `Citation Anchor: \\\\ue202turn${turn}search${i}`,\n `URL: ${r.link}`,\n r.snippet != null ? `Summary: ${r.snippet}` : '',\n r.date != null ? `Date: ${r.date}` : '',\n r.attribution != null ? `Source: ${r.attribution}` : '',\n '',\n '--- Content Highlights ---',\n ...(r.highlights ?? [])\n .filter((h) => h.text.trim().length > 0)\n .map((h) => `[Relevance: ${h.score.toFixed(2)}]\\n${h.text.trim()}`),\n '',\n ]\n .filter(Boolean)\n .join('\\n');\n });\n }\n\n // Ignoring these sections for now\n // // Top stories (news)\n // const topStores = results.topStories ?? [];\n // if (topStores.length) {\n // addSection('News Results');\n // topStores.forEach((r, i) => {\n // output += [\n // `Anchor: \\ue202turn0news${i}`,\n // `Title: ${r.title ?? '(no title)'}`,\n // `URL: ${r.link}`,\n // r.snippet != null ? `Snippet: ${r.snippet}` : '',\n // r.date != null ? `Date: ${r.date}` : '',\n // r.attribution != null ? `Source: ${r.attribution}` : '',\n // ''\n // ].filter(Boolean).join('\\n');\n // });\n // }\n\n // // Images\n // const images = results.images ?? [];\n // if (images.length) {\n // addSection('Image Results');\n // images.forEach((img, i) => {\n // output += [\n // `Anchor: \\ue202turn0image${i}`,\n // `Title: ${img.title ?? '(no title)'}`,\n // `Image URL: ${img.imageUrl}`,\n // ''\n // ].join('\\n');\n // });\n // }\n\n // Knowledge Graph\n if (results.knowledgeGraph != null) {\n addSection('Knowledge Graph');\n output += [\n `Title: ${results.knowledgeGraph.title ?? '(no title)'}`,\n results.knowledgeGraph.description != null\n ? `Description: ${results.knowledgeGraph.description}`\n : '',\n results.knowledgeGraph.type != null\n ? `Type: ${results.knowledgeGraph.type}`\n : '',\n results.knowledgeGraph.imageUrl != null\n ? `Image URL: ${results.knowledgeGraph.imageUrl}`\n : '',\n results.knowledgeGraph.attributes != null\n ? `Attributes: ${JSON.stringify(results.knowledgeGraph.attributes, null, 2)}`\n : '',\n '',\n ]\n .filter(Boolean)\n .join('\\n');\n }\n\n // Answer Box\n if (results.answerBox != null) {\n addSection('Answer Box');\n output += [\n results.answerBox.title != null\n ? `Title: ${results.answerBox.title}`\n : '',\n results.answerBox.answer != null\n ? `Answer: ${results.answerBox.answer}`\n : '',\n results.answerBox.snippet != null\n ? `Snippet: ${results.answerBox.snippet}`\n : '',\n results.answerBox.date != null ? `Date: ${results.answerBox.date}` : '',\n '',\n ]\n .filter(Boolean)\n .join('\\n');\n }\n\n // People also ask\n const peopleAlsoAsk = results.peopleAlsoAsk ?? [];\n if (peopleAlsoAsk.length) {\n addSection('People Also Ask');\n peopleAlsoAsk.forEach((p, _i) => {\n output += [`Q: ${p.question}`, `A: ${p.answer}`, '']\n .filter(Boolean)\n .join('\\n');\n });\n }\n\n return output.trim();\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -9,9 +9,25 @@ import { Constants } from '../../common/enum.mjs';
|
|
|
9
9
|
|
|
10
10
|
/* eslint-disable no-console */
|
|
11
11
|
const SearchToolSchema = z.object({
|
|
12
|
-
query: z
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
query: z.string().describe(`
|
|
13
|
+
GUIDELINES:
|
|
14
|
+
- Start broad, then narrow: Begin with key concepts, then refine with specifics
|
|
15
|
+
- Think like sources: Use terminology experts would use in the field
|
|
16
|
+
- Consider perspective: Frame queries from different viewpoints for better results
|
|
17
|
+
- Quality over quantity: A precise 3-4 word query often beats lengthy sentences
|
|
18
|
+
|
|
19
|
+
TECHNIQUES (combine for power searches):
|
|
20
|
+
- EXACT PHRASES: Use quotes ("climate change report")
|
|
21
|
+
- EXCLUDE TERMS: Use minus to remove unwanted results (-wikipedia)
|
|
22
|
+
- SITE-SPECIFIC: Restrict to websites (site:edu research)
|
|
23
|
+
- FILETYPE: Find specific documents (filetype:pdf study)
|
|
24
|
+
- OR OPERATOR: Find alternatives (electric OR hybrid cars)
|
|
25
|
+
- DATE RANGE: Recent information (data after:2020)
|
|
26
|
+
- WILDCARDS: Use * for unknown terms (how to * bread)
|
|
27
|
+
- SPECIFIC QUESTIONS: Use who/what/when/where/why/how
|
|
28
|
+
- DOMAIN TERMS: Include technical terminology for specialized topics
|
|
29
|
+
- CONCISE TERMS: Prioritize keywords over sentences
|
|
30
|
+
`.trim()),
|
|
15
31
|
});
|
|
16
32
|
const createSearchTool = (config = {}) => {
|
|
17
33
|
const { searchProvider = 'serper', serperApiKey, searxngInstanceUrl, searxngApiKey, rerankerType = 'cohere', topResults = 5, strategies = ['no_extraction'], filterContent = true, firecrawlApiKey, firecrawlApiUrl, firecrawlFormats = ['markdown', 'html'], jinaApiKey, cohereApiKey, onSearchResults: _onSearchResults, } = config;
|
|
@@ -67,27 +83,29 @@ const createSearchTool = (config = {}) => {
|
|
|
67
83
|
}
|
|
68
84
|
: undefined,
|
|
69
85
|
});
|
|
70
|
-
const
|
|
71
|
-
|
|
86
|
+
const turn = runnableConfig.toolCall?.turn ?? 0;
|
|
87
|
+
const output = formatResultsForLLM(turn, searchResult);
|
|
88
|
+
return [output, { [Constants.WEB_SEARCH]: { turn, ...searchResult } }];
|
|
72
89
|
}, {
|
|
73
90
|
name: Constants.WEB_SEARCH,
|
|
74
91
|
description: `
|
|
75
92
|
Real-time search. Results have required unique citation anchors.
|
|
76
93
|
|
|
77
94
|
Anchors:
|
|
78
|
-
- \\
|
|
95
|
+
- \\ue202turnXsearchY (web), \\ue202turnXnewsY (news), \\ue202turnXimageY (image)
|
|
96
|
+
- X = turn, Y = item number
|
|
79
97
|
|
|
80
98
|
Special Markers:
|
|
81
|
-
- \\ue203...\\ue204 —
|
|
82
|
-
- \\ue200...\\ue201 —
|
|
83
|
-
- \\ue206 — marks grouped/summary citation areas
|
|
99
|
+
- \\ue203...\\ue204 — highlight start/end of cited text (for Standalone or Group citations)
|
|
100
|
+
- \\ue200...\\ue201 — group block (e.g. \\ue200\\ue202turn0search1\\ue202turn0news2\\ue201)
|
|
84
101
|
|
|
85
102
|
**CITE EVERY NON-OBVIOUS FACT/QUOTE:**
|
|
86
|
-
|
|
87
|
-
- "Pure functions produce same output \\ue202turn0search0
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
- Group: "
|
|
103
|
+
Use anchor marker(s) immediately after the statement:
|
|
104
|
+
- Standalone: "Pure functions produce same output. \\ue202turn0search0"
|
|
105
|
+
- Standalone (multiple): "Today's News \\ue202turn0search0\\ue202turn0news0"
|
|
106
|
+
- Highlight: "\\ue203Highlight text.\\ue204\\ue202turn0news1"
|
|
107
|
+
- Group: "Sources. \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
108
|
+
- Group Highlight: "\\ue203Highlight for group.\\ue204 \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
91
109
|
- Image: "See photo \\ue202turn0image0."
|
|
92
110
|
|
|
93
111
|
**NEVER use markdown links, [1], or footnotes. CITE ONLY with anchors provided.**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.mjs","sources":["../../../../src/tools/search/tool.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport { z } from 'zod';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from './types';\nimport { createSearchAPI, createSourceProcessor } from './search';\nimport { createFirecrawlScraper } from './firecrawl';\nimport { expandHighlights } from './highlights';\nimport { formatResultsForLLM } from './format';\nimport { createReranker } from './rerankers';\nimport { Constants } from '@/common';\n\nconst SearchToolSchema = z.object({\n query: z
|
|
1
|
+
{"version":3,"file":"tool.mjs","sources":["../../../../src/tools/search/tool.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport { z } from 'zod';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from './types';\nimport { createSearchAPI, createSourceProcessor } from './search';\nimport { createFirecrawlScraper } from './firecrawl';\nimport { expandHighlights } from './highlights';\nimport { formatResultsForLLM } from './format';\nimport { createReranker } from './rerankers';\nimport { Constants } from '@/common';\n\nconst SearchToolSchema = z.object({\n query: z.string().describe(\n `\nGUIDELINES:\n- Start broad, then narrow: Begin with key concepts, then refine with specifics\n- Think like sources: Use terminology experts would use in the field\n- Consider perspective: Frame queries from different viewpoints for better results\n- Quality over quantity: A precise 3-4 word query often beats lengthy sentences\n\nTECHNIQUES (combine for power searches):\n- EXACT PHRASES: Use quotes (\"climate change report\")\n- EXCLUDE TERMS: Use minus to remove unwanted results (-wikipedia)\n- SITE-SPECIFIC: Restrict to websites (site:edu research)\n- FILETYPE: Find specific documents (filetype:pdf study)\n- OR OPERATOR: Find alternatives (electric OR hybrid cars)\n- DATE RANGE: Recent information (data after:2020)\n- WILDCARDS: Use * for unknown terms (how to * bread)\n- SPECIFIC QUESTIONS: Use who/what/when/where/why/how\n- DOMAIN TERMS: Include technical terminology for specialized topics\n- CONCISE TERMS: Prioritize keywords over sentences\n`.trim()\n ),\n});\n\nexport const createSearchTool = (\n config: t.SearchToolConfig = {}\n): DynamicStructuredTool<typeof SearchToolSchema> => {\n const {\n searchProvider = 'serper',\n serperApiKey,\n searxngInstanceUrl,\n searxngApiKey,\n rerankerType = 'cohere',\n topResults = 5,\n strategies = ['no_extraction'],\n filterContent = true,\n firecrawlApiKey,\n firecrawlApiUrl,\n firecrawlFormats = ['markdown', 'html'],\n jinaApiKey,\n cohereApiKey,\n onSearchResults: _onSearchResults,\n } = config;\n\n const searchAPI = createSearchAPI({\n searchProvider,\n serperApiKey,\n searxngInstanceUrl,\n searxngApiKey,\n });\n\n const firecrawlScraper = createFirecrawlScraper({\n apiKey: firecrawlApiKey ?? process.env.FIRECRAWL_API_KEY,\n apiUrl: firecrawlApiUrl,\n formats: firecrawlFormats,\n });\n\n const selectedReranker = createReranker({\n rerankerType,\n jinaApiKey,\n cohereApiKey,\n });\n\n if (!selectedReranker) {\n console.warn('No reranker selected. Using default ranking.');\n }\n\n const sourceProcessor = createSourceProcessor(\n {\n reranker: selectedReranker,\n topResults,\n strategies,\n filterContent,\n },\n firecrawlScraper\n );\n\n const search = async ({\n query,\n proMode = true,\n maxSources = 5,\n onSearchResults,\n }: {\n query: string;\n proMode?: boolean;\n maxSources?: number;\n onSearchResults?: (sources: t.SearchResult) => void;\n }): Promise<t.SearchResultData> => {\n try {\n const sources = await searchAPI.getSources(query);\n onSearchResults?.(sources);\n\n if (!sources.success) {\n throw new Error(sources.error ?? 'Search failed');\n }\n\n const processedSources = await sourceProcessor.processSources(\n sources,\n maxSources,\n query,\n proMode\n );\n return expandHighlights(processedSources);\n } catch (error) {\n console.error('Error in search:', error);\n return {\n organic: [],\n topStories: [],\n images: [],\n relatedSearches: [],\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n\n return tool<typeof SearchToolSchema>(\n async ({ query }, runnableConfig) => {\n const searchResult = await search({\n query,\n onSearchResults: _onSearchResults\n ? (result): void => {\n _onSearchResults(result, runnableConfig);\n }\n : undefined,\n });\n const turn = runnableConfig.toolCall?.turn ?? 0;\n const output = formatResultsForLLM(turn, searchResult);\n return [output, { [Constants.WEB_SEARCH]: { turn, ...searchResult } }];\n },\n {\n name: Constants.WEB_SEARCH,\n description: `\nReal-time search. Results have required unique citation anchors.\n\nAnchors:\n- \\\\ue202turnXsearchY (web), \\\\ue202turnXnewsY (news), \\\\ue202turnXimageY (image)\n- X = turn, Y = item number\n\nSpecial Markers:\n- \\\\ue203...\\\\ue204 — highlight start/end of cited text (for Standalone or Group citations)\n- \\\\ue200...\\\\ue201 — group block (e.g. \\\\ue200\\\\ue202turn0search1\\\\ue202turn0news2\\\\ue201)\n\n**CITE EVERY NON-OBVIOUS FACT/QUOTE:**\nUse anchor marker(s) immediately after the statement:\n- Standalone: \"Pure functions produce same output. \\\\ue202turn0search0\"\n- Standalone (multiple): \"Today's News \\\\ue202turn0search0\\\\ue202turn0news0\"\n- Highlight: \"\\\\ue203Highlight text.\\\\ue204\\\\ue202turn0news1\"\n- Group: \"Sources. \\\\ue200\\\\ue202turn0search0\\\\ue202turn0news1\\\\ue201\"\n- Group Highlight: \"\\\\ue203Highlight for group.\\\\ue204 \\\\ue200\\\\ue202turn0search0\\\\ue202turn0news1\\\\ue201\"\n- Image: \"See photo \\\\ue202turn0image0.\"\n\n**NEVER use markdown links, [1], or footnotes. CITE ONLY with anchors provided.**\n`.trim(),\n schema: SearchToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,13 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
10
10
|
handleToolErrors: boolean;
|
|
11
11
|
toolCallStepIds?: Map<string, string>;
|
|
12
12
|
errorHandler?: t.ToolNodeConstructorParams['errorHandler'];
|
|
13
|
+
private toolUsageCount;
|
|
13
14
|
constructor({ tools, toolMap, name, tags, errorHandler, toolCallStepIds, handleToolErrors, loadRuntimeTools, }: t.ToolNodeConstructorParams);
|
|
15
|
+
/**
|
|
16
|
+
* Returns a snapshot of the current tool usage counts.
|
|
17
|
+
* @returns A ReadonlyMap where keys are tool names and values are their usage counts.
|
|
18
|
+
*/
|
|
19
|
+
getToolUsageCounts(): ReadonlyMap<string, number>;
|
|
14
20
|
protected run(input: any, config: RunnableConfig): Promise<T>;
|
|
15
21
|
}
|
|
16
22
|
export declare function toolsCondition(state: BaseMessage[] | typeof MessagesAnnotation.State): 'tools' | typeof END;
|
|
@@ -11,7 +11,17 @@ export declare const fetchRandomImageTool: DynamicStructuredTool<z.ZodObject<{
|
|
|
11
11
|
input?: string | undefined;
|
|
12
12
|
}, {
|
|
13
13
|
input?: string | undefined;
|
|
14
|
-
}
|
|
14
|
+
}, (string | undefined)[] | ({
|
|
15
|
+
type: string;
|
|
16
|
+
text: string;
|
|
17
|
+
}[] | {
|
|
18
|
+
content: {
|
|
19
|
+
type: string;
|
|
20
|
+
image_url: {
|
|
21
|
+
url: string;
|
|
22
|
+
};
|
|
23
|
+
}[];
|
|
24
|
+
})[]>;
|
|
15
25
|
export declare const fetchRandomImageURL: DynamicStructuredTool<z.ZodObject<{
|
|
16
26
|
input: z.ZodOptional<z.ZodString>;
|
|
17
27
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -22,7 +32,17 @@ export declare const fetchRandomImageURL: DynamicStructuredTool<z.ZodObject<{
|
|
|
22
32
|
input?: string | undefined;
|
|
23
33
|
}, {
|
|
24
34
|
input?: string | undefined;
|
|
25
|
-
}
|
|
35
|
+
}, (string | undefined)[] | ({
|
|
36
|
+
type: string;
|
|
37
|
+
text: string;
|
|
38
|
+
}[] | {
|
|
39
|
+
content: {
|
|
40
|
+
type: string;
|
|
41
|
+
image_url: {
|
|
42
|
+
url: string;
|
|
43
|
+
};
|
|
44
|
+
}[];
|
|
45
|
+
})[]>;
|
|
26
46
|
export declare const chartTool: DynamicStructuredTool<z.ZodObject<{
|
|
27
47
|
data: z.ZodArray<z.ZodObject<{
|
|
28
48
|
label: z.ZodString;
|
|
@@ -54,5 +74,5 @@ export declare const chartTool: DynamicStructuredTool<z.ZodObject<{
|
|
|
54
74
|
value: number;
|
|
55
75
|
label: string;
|
|
56
76
|
}[];
|
|
57
|
-
}>;
|
|
77
|
+
}, string>;
|
|
58
78
|
export declare const tavilyTool: TavilySearchResults;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type * as t from './types';
|
|
2
|
-
export declare function formatResultsForLLM(results: t.SearchResultData): string;
|
|
2
|
+
export declare function formatResultsForLLM(turn: number, results: t.SearchResultData): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@librechat/agents",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.316",
|
|
4
4
|
"main": "./dist/cjs/main.cjs",
|
|
5
5
|
"module": "./dist/esm/main.mjs",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -73,13 +73,13 @@
|
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@langchain/anthropic": "^0.3.20",
|
|
76
|
-
"@langchain/aws": "^0.1.
|
|
76
|
+
"@langchain/aws": "^0.1.8",
|
|
77
77
|
"@langchain/community": "^0.3.42",
|
|
78
|
-
"@langchain/core": "^0.3.
|
|
78
|
+
"@langchain/core": "^0.3.55",
|
|
79
79
|
"@langchain/deepseek": "^0.0.1",
|
|
80
|
-
"@langchain/google-genai": "^0.2.
|
|
81
|
-
"@langchain/google-vertexai": "^0.2.
|
|
82
|
-
"@langchain/langgraph": "^0.2.
|
|
80
|
+
"@langchain/google-genai": "^0.2.8",
|
|
81
|
+
"@langchain/google-vertexai": "^0.2.8",
|
|
82
|
+
"@langchain/langgraph": "^0.2.72",
|
|
83
83
|
"@langchain/mistralai": "^0.2.0",
|
|
84
84
|
"@langchain/ollama": "^0.2.0",
|
|
85
85
|
"@langchain/openai": "^0.5.10",
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
END,
|
|
3
|
+
MessagesAnnotation,
|
|
4
|
+
isCommand,
|
|
5
|
+
isGraphInterrupt,
|
|
6
|
+
} from '@langchain/langgraph';
|
|
2
7
|
import { ToolMessage, isBaseMessage } from '@langchain/core/messages';
|
|
3
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
RunnableConfig,
|
|
10
|
+
RunnableToolLike,
|
|
11
|
+
} from '@langchain/core/runnables';
|
|
4
12
|
import type { BaseMessage, AIMessage } from '@langchain/core/messages';
|
|
5
13
|
import type { StructuredToolInterface } from '@langchain/core/tools';
|
|
6
14
|
import type * as t from '@/types';
|
|
7
|
-
import{ RunnableCallable } from '@/utils';
|
|
15
|
+
import { RunnableCallable } from '@/utils';
|
|
8
16
|
import { GraphNodeKeys } from '@/common';
|
|
9
17
|
|
|
10
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -15,6 +23,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
15
23
|
handleToolErrors = true;
|
|
16
24
|
toolCallStepIds?: Map<string, string>;
|
|
17
25
|
errorHandler?: t.ToolNodeConstructorParams['errorHandler'];
|
|
26
|
+
private toolUsageCount: Map<string, number>;
|
|
18
27
|
|
|
19
28
|
constructor({
|
|
20
29
|
tools,
|
|
@@ -28,11 +37,20 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
28
37
|
}: t.ToolNodeConstructorParams) {
|
|
29
38
|
super({ name, tags, func: (input, config) => this.run(input, config) });
|
|
30
39
|
this.tools = tools;
|
|
31
|
-
this.toolMap = toolMap ?? new Map(tools.map(tool => [tool.name, tool]));
|
|
40
|
+
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
32
41
|
this.toolCallStepIds = toolCallStepIds;
|
|
33
42
|
this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;
|
|
34
43
|
this.loadRuntimeTools = loadRuntimeTools;
|
|
35
44
|
this.errorHandler = errorHandler;
|
|
45
|
+
this.toolUsageCount = new Map<string, number>();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns a snapshot of the current tool usage counts.
|
|
50
|
+
* @returns A ReadonlyMap where keys are tool names and values are their usage counts.
|
|
51
|
+
*/
|
|
52
|
+
public getToolUsageCounts(): ReadonlyMap<string, number> {
|
|
53
|
+
return new Map(this.toolUsageCount); // Return a copy
|
|
36
54
|
}
|
|
37
55
|
|
|
38
56
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -50,7 +68,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
50
68
|
(message as AIMessage).tool_calls ?? []
|
|
51
69
|
);
|
|
52
70
|
this.tools = tools;
|
|
53
|
-
this.toolMap = toolMap ?? new Map(tools.map(tool => [tool.name, tool]));
|
|
71
|
+
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
54
72
|
}
|
|
55
73
|
const outputs = await Promise.all(
|
|
56
74
|
(message as AIMessage).tool_calls?.map(async (call) => {
|
|
@@ -59,11 +77,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
59
77
|
if (tool === undefined) {
|
|
60
78
|
throw new Error(`Tool "${call.name}" not found.`);
|
|
61
79
|
}
|
|
80
|
+
const turn = this.toolUsageCount.get(call.name) ?? 0;
|
|
81
|
+
this.toolUsageCount.set(call.name, turn + 1);
|
|
62
82
|
const args = call.args;
|
|
63
83
|
const stepId = this.toolCallStepIds?.get(call.id!);
|
|
64
84
|
const output = await tool.invoke(
|
|
65
|
-
{ ...call, args, type: 'tool_call', stepId },
|
|
66
|
-
config
|
|
85
|
+
{ ...call, args, type: 'tool_call', stepId, turn },
|
|
86
|
+
config
|
|
67
87
|
);
|
|
68
88
|
if (
|
|
69
89
|
(isBaseMessage(output) && output._getType() === 'tool') ||
|
|
@@ -86,12 +106,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
86
106
|
if (isGraphInterrupt(e)) {
|
|
87
107
|
throw e;
|
|
88
108
|
}
|
|
89
|
-
this.errorHandler?.(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
this.errorHandler?.(
|
|
110
|
+
{
|
|
111
|
+
error: e,
|
|
112
|
+
id: call.id!,
|
|
113
|
+
name: call.name,
|
|
114
|
+
input: call.args,
|
|
115
|
+
},
|
|
116
|
+
config.metadata
|
|
117
|
+
);
|
|
95
118
|
return new ToolMessage({
|
|
96
119
|
content: `Error: ${e.message}\n Please fix your mistakes.`,
|
|
97
120
|
name: call.name,
|
|
@@ -130,4 +153,4 @@ export function toolsCondition(
|
|
|
130
153
|
} else {
|
|
131
154
|
return END;
|
|
132
155
|
}
|
|
133
|
-
}
|
|
156
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type * as t from './types';
|
|
2
2
|
|
|
3
|
-
export function formatResultsForLLM(
|
|
3
|
+
export function formatResultsForLLM(
|
|
4
|
+
turn: number,
|
|
5
|
+
results: t.SearchResultData
|
|
6
|
+
): string {
|
|
4
7
|
let output = '';
|
|
5
8
|
|
|
6
9
|
const addSection = (title: string): void => {
|
|
@@ -10,11 +13,11 @@ export function formatResultsForLLM(results: t.SearchResultData): string {
|
|
|
10
13
|
// Organic (web) results
|
|
11
14
|
const organic = results.organic ?? [];
|
|
12
15
|
if (organic.length) {
|
|
13
|
-
addSection('Web Results');
|
|
16
|
+
addSection('Web Results, Turn ' + turn);
|
|
14
17
|
organic.forEach((r, i) => {
|
|
15
18
|
output += [
|
|
16
19
|
`Source ${i}: ${r.title ?? '(no title)'}`,
|
|
17
|
-
`Citation Anchor: \\
|
|
20
|
+
`Citation Anchor: \\ue202turn${turn}search${i}`,
|
|
18
21
|
`URL: ${r.link}`,
|
|
19
22
|
r.snippet != null ? `Summary: ${r.snippet}` : '',
|
|
20
23
|
r.date != null ? `Date: ${r.date}` : '',
|
package/src/tools/search/tool.ts
CHANGED
|
@@ -10,11 +10,27 @@ import { createReranker } from './rerankers';
|
|
|
10
10
|
import { Constants } from '@/common';
|
|
11
11
|
|
|
12
12
|
const SearchToolSchema = z.object({
|
|
13
|
-
query: z
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
query: z.string().describe(
|
|
14
|
+
`
|
|
15
|
+
GUIDELINES:
|
|
16
|
+
- Start broad, then narrow: Begin with key concepts, then refine with specifics
|
|
17
|
+
- Think like sources: Use terminology experts would use in the field
|
|
18
|
+
- Consider perspective: Frame queries from different viewpoints for better results
|
|
19
|
+
- Quality over quantity: A precise 3-4 word query often beats lengthy sentences
|
|
20
|
+
|
|
21
|
+
TECHNIQUES (combine for power searches):
|
|
22
|
+
- EXACT PHRASES: Use quotes ("climate change report")
|
|
23
|
+
- EXCLUDE TERMS: Use minus to remove unwanted results (-wikipedia)
|
|
24
|
+
- SITE-SPECIFIC: Restrict to websites (site:edu research)
|
|
25
|
+
- FILETYPE: Find specific documents (filetype:pdf study)
|
|
26
|
+
- OR OPERATOR: Find alternatives (electric OR hybrid cars)
|
|
27
|
+
- DATE RANGE: Recent information (data after:2020)
|
|
28
|
+
- WILDCARDS: Use * for unknown terms (how to * bread)
|
|
29
|
+
- SPECIFIC QUESTIONS: Use who/what/when/where/why/how
|
|
30
|
+
- DOMAIN TERMS: Include technical terminology for specialized topics
|
|
31
|
+
- CONCISE TERMS: Prioritize keywords over sentences
|
|
32
|
+
`.trim()
|
|
33
|
+
),
|
|
18
34
|
});
|
|
19
35
|
|
|
20
36
|
export const createSearchTool = (
|
|
@@ -118,8 +134,9 @@ export const createSearchTool = (
|
|
|
118
134
|
}
|
|
119
135
|
: undefined,
|
|
120
136
|
});
|
|
121
|
-
const
|
|
122
|
-
|
|
137
|
+
const turn = runnableConfig.toolCall?.turn ?? 0;
|
|
138
|
+
const output = formatResultsForLLM(turn, searchResult);
|
|
139
|
+
return [output, { [Constants.WEB_SEARCH]: { turn, ...searchResult } }];
|
|
123
140
|
},
|
|
124
141
|
{
|
|
125
142
|
name: Constants.WEB_SEARCH,
|
|
@@ -127,19 +144,20 @@ export const createSearchTool = (
|
|
|
127
144
|
Real-time search. Results have required unique citation anchors.
|
|
128
145
|
|
|
129
146
|
Anchors:
|
|
130
|
-
- \\
|
|
147
|
+
- \\ue202turnXsearchY (web), \\ue202turnXnewsY (news), \\ue202turnXimageY (image)
|
|
148
|
+
- X = turn, Y = item number
|
|
131
149
|
|
|
132
150
|
Special Markers:
|
|
133
|
-
- \\ue203...\\ue204 —
|
|
134
|
-
- \\ue200...\\ue201 —
|
|
135
|
-
- \\ue206 — marks grouped/summary citation areas
|
|
151
|
+
- \\ue203...\\ue204 — highlight start/end of cited text (for Standalone or Group citations)
|
|
152
|
+
- \\ue200...\\ue201 — group block (e.g. \\ue200\\ue202turn0search1\\ue202turn0news2\\ue201)
|
|
136
153
|
|
|
137
154
|
**CITE EVERY NON-OBVIOUS FACT/QUOTE:**
|
|
138
|
-
|
|
139
|
-
- "Pure functions produce same output \\ue202turn0search0
|
|
140
|
-
-
|
|
141
|
-
-
|
|
142
|
-
- Group: "
|
|
155
|
+
Use anchor marker(s) immediately after the statement:
|
|
156
|
+
- Standalone: "Pure functions produce same output. \\ue202turn0search0"
|
|
157
|
+
- Standalone (multiple): "Today's News \\ue202turn0search0\\ue202turn0news0"
|
|
158
|
+
- Highlight: "\\ue203Highlight text.\\ue204\\ue202turn0news1"
|
|
159
|
+
- Group: "Sources. \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
160
|
+
- Group Highlight: "\\ue203Highlight for group.\\ue204 \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
|
|
143
161
|
- Image: "See photo \\ue202turn0image0."
|
|
144
162
|
|
|
145
163
|
**NEVER use markdown links, [1], or footnotes. CITE ONLY with anchors provided.**
|