@librechat/agents 2.4.30 → 2.4.33

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.
Files changed (124) hide show
  1. package/dist/cjs/common/enum.cjs +1 -0
  2. package/dist/cjs/common/enum.cjs.map +1 -1
  3. package/dist/cjs/events.cjs +3 -3
  4. package/dist/cjs/events.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +2 -1
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/main.cjs +7 -2
  8. package/dist/cjs/main.cjs.map +1 -1
  9. package/dist/cjs/messages/ids.cjs +23 -0
  10. package/dist/cjs/messages/ids.cjs.map +1 -0
  11. package/dist/cjs/splitStream.cjs +2 -1
  12. package/dist/cjs/splitStream.cjs.map +1 -1
  13. package/dist/cjs/stream.cjs +87 -154
  14. package/dist/cjs/stream.cjs.map +1 -1
  15. package/dist/cjs/tools/ToolNode.cjs +14 -3
  16. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  17. package/dist/cjs/tools/handlers.cjs +144 -0
  18. package/dist/cjs/tools/handlers.cjs.map +1 -0
  19. package/dist/cjs/tools/search/content.cjs +140 -0
  20. package/dist/cjs/tools/search/content.cjs.map +1 -0
  21. package/dist/cjs/tools/search/firecrawl.cjs +131 -0
  22. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -0
  23. package/dist/cjs/tools/search/format.cjs +203 -0
  24. package/dist/cjs/tools/search/format.cjs.map +1 -0
  25. package/dist/cjs/tools/search/highlights.cjs +245 -0
  26. package/dist/cjs/tools/search/highlights.cjs.map +1 -0
  27. package/dist/cjs/tools/search/rerankers.cjs +194 -0
  28. package/dist/cjs/tools/search/rerankers.cjs.map +1 -0
  29. package/dist/cjs/tools/search/schema.cjs +70 -0
  30. package/dist/cjs/tools/search/schema.cjs.map +1 -0
  31. package/dist/cjs/tools/search/search.cjs +491 -0
  32. package/dist/cjs/tools/search/search.cjs.map +1 -0
  33. package/dist/cjs/tools/search/tool.cjs +292 -0
  34. package/dist/cjs/tools/search/tool.cjs.map +1 -0
  35. package/dist/cjs/tools/search/utils.cjs +66 -0
  36. package/dist/cjs/tools/search/utils.cjs.map +1 -0
  37. package/dist/esm/common/enum.mjs +1 -0
  38. package/dist/esm/common/enum.mjs.map +1 -1
  39. package/dist/esm/events.mjs +1 -1
  40. package/dist/esm/events.mjs.map +1 -1
  41. package/dist/esm/graphs/Graph.mjs +2 -1
  42. package/dist/esm/graphs/Graph.mjs.map +1 -1
  43. package/dist/esm/main.mjs +4 -1
  44. package/dist/esm/main.mjs.map +1 -1
  45. package/dist/esm/messages/ids.mjs +21 -0
  46. package/dist/esm/messages/ids.mjs.map +1 -0
  47. package/dist/esm/splitStream.mjs +2 -1
  48. package/dist/esm/splitStream.mjs.map +1 -1
  49. package/dist/esm/stream.mjs +87 -152
  50. package/dist/esm/stream.mjs.map +1 -1
  51. package/dist/esm/tools/ToolNode.mjs +14 -3
  52. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  53. package/dist/esm/tools/handlers.mjs +141 -0
  54. package/dist/esm/tools/handlers.mjs.map +1 -0
  55. package/dist/esm/tools/search/content.mjs +119 -0
  56. package/dist/esm/tools/search/content.mjs.map +1 -0
  57. package/dist/esm/tools/search/firecrawl.mjs +128 -0
  58. package/dist/esm/tools/search/firecrawl.mjs.map +1 -0
  59. package/dist/esm/tools/search/format.mjs +201 -0
  60. package/dist/esm/tools/search/format.mjs.map +1 -0
  61. package/dist/esm/tools/search/highlights.mjs +243 -0
  62. package/dist/esm/tools/search/highlights.mjs.map +1 -0
  63. package/dist/esm/tools/search/rerankers.mjs +188 -0
  64. package/dist/esm/tools/search/rerankers.mjs.map +1 -0
  65. package/dist/esm/tools/search/schema.mjs +61 -0
  66. package/dist/esm/tools/search/schema.mjs.map +1 -0
  67. package/dist/esm/tools/search/search.mjs +488 -0
  68. package/dist/esm/tools/search/search.mjs.map +1 -0
  69. package/dist/esm/tools/search/tool.mjs +290 -0
  70. package/dist/esm/tools/search/tool.mjs.map +1 -0
  71. package/dist/esm/tools/search/utils.mjs +61 -0
  72. package/dist/esm/tools/search/utils.mjs.map +1 -0
  73. package/dist/types/common/enum.d.ts +1 -0
  74. package/dist/types/graphs/Graph.d.ts +1 -1
  75. package/dist/types/index.d.ts +2 -0
  76. package/dist/types/messages/ids.d.ts +3 -0
  77. package/dist/types/messages/index.d.ts +1 -0
  78. package/dist/types/scripts/search.d.ts +1 -0
  79. package/dist/types/stream.d.ts +0 -8
  80. package/dist/types/tools/ToolNode.d.ts +6 -0
  81. package/dist/types/tools/example.d.ts +23 -3
  82. package/dist/types/tools/handlers.d.ts +8 -0
  83. package/dist/types/tools/search/content.d.ts +4 -0
  84. package/dist/types/tools/search/firecrawl.d.ts +38 -0
  85. package/dist/types/tools/search/format.d.ts +5 -0
  86. package/dist/types/tools/search/highlights.d.ts +13 -0
  87. package/dist/types/tools/search/index.d.ts +2 -0
  88. package/dist/types/tools/search/rerankers.d.ts +36 -0
  89. package/dist/types/tools/search/schema.d.ts +16 -0
  90. package/dist/types/tools/search/search.d.ts +9 -0
  91. package/dist/types/tools/search/test.d.ts +1 -0
  92. package/dist/types/tools/search/tool.d.ts +33 -0
  93. package/dist/types/tools/search/types.d.ts +540 -0
  94. package/dist/types/tools/search/utils.d.ts +10 -0
  95. package/package.json +10 -7
  96. package/src/common/enum.ts +1 -0
  97. package/src/events.ts +49 -15
  98. package/src/graphs/Graph.ts +6 -2
  99. package/src/index.ts +2 -0
  100. package/src/messages/ids.ts +26 -0
  101. package/src/messages/index.ts +1 -0
  102. package/src/scripts/search.ts +146 -0
  103. package/src/splitStream.test.ts +132 -71
  104. package/src/splitStream.ts +2 -1
  105. package/src/stream.ts +94 -183
  106. package/src/tools/ToolNode.ts +37 -14
  107. package/src/tools/handlers.ts +167 -0
  108. package/src/tools/search/content.test.ts +173 -0
  109. package/src/tools/search/content.ts +147 -0
  110. package/src/tools/search/firecrawl.ts +158 -0
  111. package/src/tools/search/format.ts +252 -0
  112. package/src/tools/search/highlights.ts +320 -0
  113. package/src/tools/search/index.ts +2 -0
  114. package/src/tools/search/output.md +2775 -0
  115. package/src/tools/search/rerankers.ts +269 -0
  116. package/src/tools/search/schema.ts +63 -0
  117. package/src/tools/search/search.ts +680 -0
  118. package/src/tools/search/test.html +884 -0
  119. package/src/tools/search/test.md +643 -0
  120. package/src/tools/search/test.ts +159 -0
  121. package/src/tools/search/tool.ts +427 -0
  122. package/src/tools/search/types.ts +621 -0
  123. package/src/tools/search/utils.ts +79 -0
  124. package/src/utils/llmConfig.ts +1 -1
@@ -0,0 +1,128 @@
1
+ import axios from 'axios';
2
+ import { processContent } from './content.mjs';
3
+ import { createDefaultLogger } from './utils.mjs';
4
+
5
+ /**
6
+ * Firecrawl scraper implementation
7
+ * Uses the Firecrawl API to scrape web pages
8
+ */
9
+ class FirecrawlScraper {
10
+ apiKey;
11
+ apiUrl;
12
+ defaultFormats;
13
+ timeout;
14
+ logger;
15
+ constructor(config = {}) {
16
+ this.apiKey = config.apiKey ?? process.env.FIRECRAWL_API_KEY ?? '';
17
+ const baseUrl = config.apiUrl ??
18
+ process.env.FIRECRAWL_BASE_URL ??
19
+ 'https://api.firecrawl.dev';
20
+ this.apiUrl = `${baseUrl.replace(/\/+$/, '')}/v1/scrape`;
21
+ this.defaultFormats = config.formats ?? ['markdown', 'html'];
22
+ this.timeout = config.timeout ?? 15000;
23
+ this.logger = config.logger || createDefaultLogger();
24
+ if (!this.apiKey) {
25
+ this.logger.warn('FIRECRAWL_API_KEY is not set. Scraping will not work.');
26
+ }
27
+ this.logger.debug(`Firecrawl scraper initialized with API URL: ${this.apiUrl}`);
28
+ }
29
+ /**
30
+ * Scrape a single URL
31
+ * @param url URL to scrape
32
+ * @param options Scrape options
33
+ * @returns Scrape response
34
+ */
35
+ async scrapeUrl(url, options = {}) {
36
+ if (!this.apiKey) {
37
+ return [
38
+ url,
39
+ {
40
+ success: false,
41
+ error: 'FIRECRAWL_API_KEY is not set',
42
+ },
43
+ ];
44
+ }
45
+ try {
46
+ const response = await axios.post(this.apiUrl, {
47
+ url,
48
+ formats: options.formats || this.defaultFormats,
49
+ includeTags: options.includeTags,
50
+ excludeTags: options.excludeTags,
51
+ headers: options.headers,
52
+ waitFor: options.waitFor,
53
+ timeout: options.timeout ?? this.timeout,
54
+ }, {
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ Authorization: `Bearer ${this.apiKey}`,
58
+ },
59
+ timeout: this.timeout,
60
+ });
61
+ return [url, response.data];
62
+ }
63
+ catch (error) {
64
+ const errorMessage = error instanceof Error ? error.message : String(error);
65
+ return [
66
+ url,
67
+ {
68
+ success: false,
69
+ error: `Firecrawl API request failed: ${errorMessage}`,
70
+ },
71
+ ];
72
+ }
73
+ }
74
+ /**
75
+ * Extract content from scrape response
76
+ * @param response Scrape response
77
+ * @returns Extracted content or empty string if not available
78
+ */
79
+ extractContent(response) {
80
+ if (!response.success || !response.data) {
81
+ return ['', undefined];
82
+ }
83
+ if (response.data.markdown != null && response.data.html != null) {
84
+ try {
85
+ const { markdown, ...rest } = processContent(response.data.html, response.data.markdown);
86
+ return [markdown, rest];
87
+ }
88
+ catch (error) {
89
+ this.logger.error('Error processing content:', error);
90
+ return [response.data.markdown, undefined];
91
+ }
92
+ }
93
+ else if (response.data.markdown != null) {
94
+ return [response.data.markdown, undefined];
95
+ }
96
+ // Fall back to HTML content
97
+ if (response.data.html != null) {
98
+ return [response.data.html, undefined];
99
+ }
100
+ // Fall back to raw HTML content
101
+ if (response.data.rawHtml != null) {
102
+ return [response.data.rawHtml, undefined];
103
+ }
104
+ return ['', undefined];
105
+ }
106
+ /**
107
+ * Extract metadata from scrape response
108
+ * @param response Scrape response
109
+ * @returns Metadata object
110
+ */
111
+ extractMetadata(response) {
112
+ if (!response.success || !response.data || !response.data.metadata) {
113
+ return {};
114
+ }
115
+ return response.data.metadata;
116
+ }
117
+ }
118
+ /**
119
+ * Create a Firecrawl scraper instance
120
+ * @param config Scraper configuration
121
+ * @returns Firecrawl scraper instance
122
+ */
123
+ const createFirecrawlScraper = (config = {}) => {
124
+ return new FirecrawlScraper(config);
125
+ };
126
+
127
+ export { FirecrawlScraper, createFirecrawlScraper };
128
+ //# sourceMappingURL=firecrawl.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firecrawl.mjs","sources":["../../../../src/tools/search/firecrawl.ts"],"sourcesContent":["import axios from 'axios';\nimport { processContent } from './content';\nimport type * as t from './types';\nimport { createDefaultLogger } from './utils';\n\n/**\n * Firecrawl scraper implementation\n * Uses the Firecrawl API to scrape web pages\n */\nexport class FirecrawlScraper {\n private apiKey: string;\n private apiUrl: string;\n private defaultFormats: string[];\n private timeout: number;\n private logger: t.Logger;\n\n constructor(config: t.FirecrawlScraperConfig = {}) {\n this.apiKey = config.apiKey ?? process.env.FIRECRAWL_API_KEY ?? '';\n\n const baseUrl =\n config.apiUrl ??\n process.env.FIRECRAWL_BASE_URL ??\n 'https://api.firecrawl.dev';\n this.apiUrl = `${baseUrl.replace(/\\/+$/, '')}/v1/scrape`;\n\n this.defaultFormats = config.formats ?? ['markdown', 'html'];\n this.timeout = config.timeout ?? 15000;\n\n this.logger = config.logger || createDefaultLogger();\n\n if (!this.apiKey) {\n this.logger.warn('FIRECRAWL_API_KEY is not set. Scraping will not work.');\n }\n\n this.logger.debug(\n `Firecrawl scraper initialized with API URL: ${this.apiUrl}`\n );\n }\n\n /**\n * Scrape a single URL\n * @param url URL to scrape\n * @param options Scrape options\n * @returns Scrape response\n */\n async scrapeUrl(\n url: string,\n options: t.FirecrawlScrapeOptions = {}\n ): Promise<[string, t.FirecrawlScrapeResponse]> {\n if (!this.apiKey) {\n return [\n url,\n {\n success: false,\n error: 'FIRECRAWL_API_KEY is not set',\n },\n ];\n }\n\n try {\n const response = await axios.post(\n this.apiUrl,\n {\n url,\n formats: options.formats || this.defaultFormats,\n includeTags: options.includeTags,\n excludeTags: options.excludeTags,\n headers: options.headers,\n waitFor: options.waitFor,\n timeout: options.timeout ?? this.timeout,\n },\n {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n timeout: this.timeout,\n }\n );\n\n return [url, response.data];\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return [\n url,\n {\n success: false,\n error: `Firecrawl API request failed: ${errorMessage}`,\n },\n ];\n }\n }\n\n /**\n * Extract content from scrape response\n * @param response Scrape response\n * @returns Extracted content or empty string if not available\n */\n extractContent(\n response: t.FirecrawlScrapeResponse\n ): [string, undefined | t.References] {\n if (!response.success || !response.data) {\n return ['', undefined];\n }\n\n if (response.data.markdown != null && response.data.html != null) {\n try {\n const { markdown, ...rest } = processContent(\n response.data.html,\n response.data.markdown\n );\n return [markdown, rest];\n } catch (error) {\n this.logger.error('Error processing content:', error);\n return [response.data.markdown, undefined];\n }\n } else if (response.data.markdown != null) {\n return [response.data.markdown, undefined];\n }\n\n // Fall back to HTML content\n if (response.data.html != null) {\n return [response.data.html, undefined];\n }\n\n // Fall back to raw HTML content\n if (response.data.rawHtml != null) {\n return [response.data.rawHtml, undefined];\n }\n\n return ['', undefined];\n }\n\n /**\n * Extract metadata from scrape response\n * @param response Scrape response\n * @returns Metadata object\n */\n extractMetadata(response: t.FirecrawlScrapeResponse): t.ScrapeMetadata {\n if (!response.success || !response.data || !response.data.metadata) {\n return {};\n }\n\n return response.data.metadata;\n }\n}\n\n/**\n * Create a Firecrawl scraper instance\n * @param config Scraper configuration\n * @returns Firecrawl scraper instance\n */\nexport const createFirecrawlScraper = (\n config: t.FirecrawlScraperConfig = {}\n): FirecrawlScraper => {\n return new FirecrawlScraper(config);\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,201 @@
1
+ import { getDomainName, fileExtRegex } from './utils.mjs';
2
+
3
+ function addHighlightSection() {
4
+ return ['\n## Highlights', ''];
5
+ }
6
+ // Helper function to format a source (organic or top story)
7
+ function formatSource(source, index, turn, sourceType, references) {
8
+ /** Array of all lines to include in the output */
9
+ const outputLines = [];
10
+ // Add the title
11
+ outputLines.push(`# ${sourceType.charAt(0).toUpperCase() + sourceType.slice(1)} ${index}: ${source.title != null && source.title ? `"${source.title}"` : '(no title)'}`);
12
+ outputLines.push(`\nAnchor: \\ue202turn${turn}${sourceType}${index}`);
13
+ outputLines.push(`URL: ${source.link}`);
14
+ // Add optional fields
15
+ if ('snippet' in source && source.snippet != null) {
16
+ outputLines.push(`Summary: ${source.snippet}`);
17
+ }
18
+ if (source.date != null) {
19
+ outputLines.push(`Date: ${source.date}`);
20
+ }
21
+ if (source.attribution != null) {
22
+ outputLines.push(`Source: ${source.attribution}`);
23
+ }
24
+ // Add highlight section or empty line
25
+ if ((source.highlights?.length ?? 0) > 0) {
26
+ outputLines.push(...addHighlightSection());
27
+ }
28
+ else {
29
+ outputLines.push('');
30
+ }
31
+ // Process highlights if they exist
32
+ (source.highlights ?? [])
33
+ .filter((h) => h.text.trim().length > 0)
34
+ .forEach((h, hIndex) => {
35
+ outputLines.push(`### Highlight ${hIndex + 1} [Relevance: ${h.score.toFixed(2)}]`);
36
+ outputLines.push('');
37
+ outputLines.push('```text');
38
+ outputLines.push(h.text.trim());
39
+ outputLines.push('```');
40
+ outputLines.push('');
41
+ if (h.references != null && h.references.length) {
42
+ let hasHeader = false;
43
+ const refLines = [];
44
+ for (let j = 0; j < h.references.length; j++) {
45
+ const ref = h.references[j];
46
+ if (ref.reference.originalUrl.includes('mailto:')) {
47
+ continue;
48
+ }
49
+ references.push({
50
+ type: ref.type,
51
+ link: ref.reference.originalUrl,
52
+ attribution: getDomainName(ref.reference.originalUrl),
53
+ title: (((ref.reference.title ?? '') || ref.reference.text) ??
54
+ '').split('\n')[0],
55
+ });
56
+ if (ref.type !== 'link') {
57
+ continue;
58
+ }
59
+ if (fileExtRegex.test(ref.reference.originalUrl)) {
60
+ continue;
61
+ }
62
+ if (!hasHeader) {
63
+ refLines.push('Core References:');
64
+ hasHeader = true;
65
+ }
66
+ refLines.push(`- ${ref.type}#${ref.originalIndex + 1}: ${ref.reference.originalUrl}`);
67
+ refLines.push(`\t- Anchor: \\ue202turn${turn}ref${references.length - 1}`);
68
+ }
69
+ if (hasHeader) {
70
+ outputLines.push(...refLines);
71
+ outputLines.push('');
72
+ }
73
+ }
74
+ if (hIndex < (source.highlights?.length ?? 0) - 1) {
75
+ outputLines.push('---');
76
+ outputLines.push('');
77
+ }
78
+ });
79
+ outputLines.push('');
80
+ return outputLines.join('\n');
81
+ }
82
+ function formatResultsForLLM(turn, results) {
83
+ /** Array to collect all output lines */
84
+ const outputLines = [];
85
+ const addSection = (title) => {
86
+ outputLines.push('');
87
+ outputLines.push(`=== ${title} ===`);
88
+ outputLines.push('');
89
+ };
90
+ const references = [];
91
+ // Organic (web) results
92
+ if (results.organic?.length != null && results.organic.length > 0) {
93
+ addSection(`Web Results, Turn ${turn}`);
94
+ for (let i = 0; i < results.organic.length; i++) {
95
+ const r = results.organic[i];
96
+ outputLines.push(formatSource(r, i, turn, 'search', references));
97
+ delete results.organic[i].highlights;
98
+ }
99
+ }
100
+ // Top stories (news)
101
+ const topStories = results.topStories ?? [];
102
+ if (topStories.length) {
103
+ addSection('News Results');
104
+ for (let i = 0; i < topStories.length; i++) {
105
+ const r = topStories[i];
106
+ outputLines.push(formatSource(r, i, turn, 'news', references));
107
+ if (results.topStories?.[i]?.highlights) {
108
+ delete results.topStories[i].highlights;
109
+ }
110
+ }
111
+ }
112
+ // // Images
113
+ // const images = results.images ?? [];
114
+ // if (images.length) {
115
+ // addSection('Image Results');
116
+ // const imageLines = images.map((img, i) => [
117
+ // `Anchor: \ue202turn0image${i}`,
118
+ // `Title: ${img.title ?? '(no title)'}`,
119
+ // `Image URL: ${img.imageUrl}`,
120
+ // ''
121
+ // ].join('\n'));
122
+ // outputLines.push(imageLines.join('\n'));
123
+ // }
124
+ // Knowledge Graph
125
+ if (results.knowledgeGraph != null) {
126
+ addSection('Knowledge Graph');
127
+ const kgLines = [
128
+ `**Title:** ${results.knowledgeGraph.title ?? '(no title)'}`,
129
+ results.knowledgeGraph.type != null
130
+ ? `**Type:** ${results.knowledgeGraph.type}`
131
+ : '',
132
+ results.knowledgeGraph.description != null
133
+ ? `**Description:** ${results.knowledgeGraph.description}`
134
+ : '',
135
+ results.knowledgeGraph.descriptionSource != null
136
+ ? `**Description Source:** ${results.knowledgeGraph.descriptionSource}`
137
+ : '',
138
+ results.knowledgeGraph.descriptionLink != null
139
+ ? `**Description Link:** ${results.knowledgeGraph.descriptionLink}`
140
+ : '',
141
+ results.knowledgeGraph.imageUrl != null
142
+ ? `**Image URL:** ${results.knowledgeGraph.imageUrl}`
143
+ : '',
144
+ results.knowledgeGraph.website != null
145
+ ? `**Website:** ${results.knowledgeGraph.website}`
146
+ : '',
147
+ results.knowledgeGraph.attributes != null
148
+ ? `**Attributes:**\n\`\`\`json\n${JSON.stringify(results.knowledgeGraph.attributes, null, 2)}\n\`\`\``
149
+ : '',
150
+ '',
151
+ ].filter(Boolean);
152
+ outputLines.push(kgLines.join('\n\n'));
153
+ }
154
+ // Answer Box
155
+ if (results.answerBox != null) {
156
+ addSection('Answer Box');
157
+ const abLines = [
158
+ results.answerBox.title != null
159
+ ? `**Title:** ${results.answerBox.title}`
160
+ : '',
161
+ results.answerBox.snippet != null
162
+ ? `**Snippet:** ${results.answerBox.snippet}`
163
+ : '',
164
+ results.answerBox.snippetHighlighted != null
165
+ ? `**Snippet Highlighted:** ${results.answerBox.snippetHighlighted
166
+ .map((s) => `\`${s}\``)
167
+ .join(' ')}`
168
+ : '',
169
+ results.answerBox.link != null
170
+ ? `**Link:** ${results.answerBox.link}`
171
+ : '',
172
+ '',
173
+ ].filter(Boolean);
174
+ outputLines.push(abLines.join('\n\n'));
175
+ }
176
+ // People also ask
177
+ const peopleAlsoAsk = results.peopleAlsoAsk ?? [];
178
+ if (peopleAlsoAsk.length) {
179
+ addSection('People Also Ask');
180
+ const paaLines = [];
181
+ peopleAlsoAsk.forEach((p, i) => {
182
+ const questionLines = [
183
+ `### Question ${i + 1}:`,
184
+ `"${p.question}"`,
185
+ `${p.snippet != null && p.snippet ? `Snippet: ${p.snippet}` : ''}`,
186
+ `${p.title != null && p.title ? `Title: ${p.title}` : ''}`,
187
+ `${p.link != null && p.link ? `Link: ${p.link}` : ''}`,
188
+ '',
189
+ ].filter(Boolean);
190
+ paaLines.push(questionLines.join('\n\n'));
191
+ });
192
+ outputLines.push(paaLines.join(''));
193
+ }
194
+ return {
195
+ output: outputLines.join('\n').trim(),
196
+ references,
197
+ };
198
+ }
199
+
200
+ export { formatResultsForLLM };
201
+ //# sourceMappingURL=format.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.mjs","sources":["../../../../src/tools/search/format.ts"],"sourcesContent":["import type * as t from './types';\nimport { getDomainName, fileExtRegex } from './utils';\n\nfunction addHighlightSection(): string[] {\n return ['\\n## Highlights', ''];\n}\n\n// Helper function to format a source (organic or top story)\nfunction formatSource(\n source: t.ValidSource,\n index: number,\n turn: number,\n sourceType: 'search' | 'news',\n references: t.ResultReference[]\n): string {\n /** Array of all lines to include in the output */\n const outputLines: string[] = [];\n\n // Add the title\n outputLines.push(\n `# ${sourceType.charAt(0).toUpperCase() + sourceType.slice(1)} ${index}: ${source.title != null && source.title ? `\"${source.title}\"` : '(no title)'}`\n );\n outputLines.push(`\\nAnchor: \\\\ue202turn${turn}${sourceType}${index}`);\n outputLines.push(`URL: ${source.link}`);\n\n // Add optional fields\n if ('snippet' in source && source.snippet != null) {\n outputLines.push(`Summary: ${source.snippet}`);\n }\n\n if (source.date != null) {\n outputLines.push(`Date: ${source.date}`);\n }\n\n if (source.attribution != null) {\n outputLines.push(`Source: ${source.attribution}`);\n }\n\n // Add highlight section or empty line\n if ((source.highlights?.length ?? 0) > 0) {\n outputLines.push(...addHighlightSection());\n } else {\n outputLines.push('');\n }\n\n // Process highlights if they exist\n (source.highlights ?? [])\n .filter((h) => h.text.trim().length > 0)\n .forEach((h, hIndex) => {\n outputLines.push(\n `### Highlight ${hIndex + 1} [Relevance: ${h.score.toFixed(2)}]`\n );\n outputLines.push('');\n outputLines.push('```text');\n outputLines.push(h.text.trim());\n outputLines.push('```');\n outputLines.push('');\n\n if (h.references != null && h.references.length) {\n let hasHeader = false;\n const refLines: string[] = [];\n\n for (let j = 0; j < h.references.length; j++) {\n const ref = h.references[j];\n if (ref.reference.originalUrl.includes('mailto:')) {\n continue;\n }\n references.push({\n type: ref.type,\n link: ref.reference.originalUrl,\n attribution: getDomainName(ref.reference.originalUrl),\n title: (\n ((ref.reference.title ?? '') || ref.reference.text) ??\n ''\n ).split('\\n')[0],\n });\n\n if (ref.type !== 'link') {\n continue;\n }\n\n if (fileExtRegex.test(ref.reference.originalUrl)) {\n continue;\n }\n\n if (!hasHeader) {\n refLines.push('Core References:');\n hasHeader = true;\n }\n\n refLines.push(\n `- ${ref.type}#${ref.originalIndex + 1}: ${ref.reference.originalUrl}`\n );\n refLines.push(\n `\\t- Anchor: \\\\ue202turn${turn}ref${references.length - 1}`\n );\n }\n\n if (hasHeader) {\n outputLines.push(...refLines);\n outputLines.push('');\n }\n }\n\n if (hIndex < (source.highlights?.length ?? 0) - 1) {\n outputLines.push('---');\n outputLines.push('');\n }\n });\n\n outputLines.push('');\n return outputLines.join('\\n');\n}\n\nexport function formatResultsForLLM(\n turn: number,\n results: t.SearchResultData\n): { output: string; references: t.ResultReference[] } {\n /** Array to collect all output lines */\n const outputLines: string[] = [];\n\n const addSection = (title: string): void => {\n outputLines.push('');\n outputLines.push(`=== ${title} ===`);\n outputLines.push('');\n };\n\n const references: t.ResultReference[] = [];\n\n // Organic (web) results\n if (results.organic?.length != null && results.organic.length > 0) {\n addSection(`Web Results, Turn ${turn}`);\n for (let i = 0; i < results.organic.length; i++) {\n const r = results.organic[i];\n outputLines.push(formatSource(r, i, turn, 'search', references));\n delete results.organic[i].highlights;\n }\n }\n\n // Top stories (news)\n const topStories = results.topStories ?? [];\n if (topStories.length) {\n addSection('News Results');\n for (let i = 0; i < topStories.length; i++) {\n const r = topStories[i];\n outputLines.push(formatSource(r, i, turn, 'news', references));\n if (results.topStories?.[i]?.highlights) {\n delete results.topStories[i].highlights;\n }\n }\n }\n\n // // Images\n // const images = results.images ?? [];\n // if (images.length) {\n // addSection('Image Results');\n // const imageLines = images.map((img, i) => [\n // `Anchor: \\ue202turn0image${i}`,\n // `Title: ${img.title ?? '(no title)'}`,\n // `Image URL: ${img.imageUrl}`,\n // ''\n // ].join('\\n'));\n // outputLines.push(imageLines.join('\\n'));\n // }\n\n // Knowledge Graph\n if (results.knowledgeGraph != null) {\n addSection('Knowledge Graph');\n const kgLines = [\n `**Title:** ${results.knowledgeGraph.title ?? '(no title)'}`,\n results.knowledgeGraph.type != null\n ? `**Type:** ${results.knowledgeGraph.type}`\n : '',\n results.knowledgeGraph.description != null\n ? `**Description:** ${results.knowledgeGraph.description}`\n : '',\n results.knowledgeGraph.descriptionSource != null\n ? `**Description Source:** ${results.knowledgeGraph.descriptionSource}`\n : '',\n results.knowledgeGraph.descriptionLink != null\n ? `**Description Link:** ${results.knowledgeGraph.descriptionLink}`\n : '',\n results.knowledgeGraph.imageUrl != null\n ? `**Image URL:** ${results.knowledgeGraph.imageUrl}`\n : '',\n results.knowledgeGraph.website != null\n ? `**Website:** ${results.knowledgeGraph.website}`\n : '',\n results.knowledgeGraph.attributes != null\n ? `**Attributes:**\\n\\`\\`\\`json\\n${JSON.stringify(\n results.knowledgeGraph.attributes,\n null,\n 2\n )}\\n\\`\\`\\``\n : '',\n '',\n ].filter(Boolean);\n\n outputLines.push(kgLines.join('\\n\\n'));\n }\n\n // Answer Box\n if (results.answerBox != null) {\n addSection('Answer Box');\n const abLines = [\n results.answerBox.title != null\n ? `**Title:** ${results.answerBox.title}`\n : '',\n results.answerBox.snippet != null\n ? `**Snippet:** ${results.answerBox.snippet}`\n : '',\n results.answerBox.snippetHighlighted != null\n ? `**Snippet Highlighted:** ${results.answerBox.snippetHighlighted\n .map((s) => `\\`${s}\\``)\n .join(' ')}`\n : '',\n results.answerBox.link != null\n ? `**Link:** ${results.answerBox.link}`\n : '',\n '',\n ].filter(Boolean);\n\n outputLines.push(abLines.join('\\n\\n'));\n }\n\n // People also ask\n const peopleAlsoAsk = results.peopleAlsoAsk ?? [];\n if (peopleAlsoAsk.length) {\n addSection('People Also Ask');\n\n const paaLines: string[] = [];\n peopleAlsoAsk.forEach((p, i) => {\n const questionLines = [\n `### Question ${i + 1}:`,\n `\"${p.question}\"`,\n `${p.snippet != null && p.snippet ? `Snippet: ${p.snippet}` : ''}`,\n `${p.title != null && p.title ? `Title: ${p.title}` : ''}`,\n `${p.link != null && p.link ? `Link: ${p.link}` : ''}`,\n '',\n ].filter(Boolean);\n\n paaLines.push(questionLines.join('\\n\\n'));\n });\n\n outputLines.push(paaLines.join(''));\n }\n\n return {\n output: outputLines.join('\\n').trim(),\n references,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,243 @@
1
+ // 2. Pre-compile all regular expressions (only do this once)
2
+ // Group patterns by priority for early returns
3
+ const priorityPatterns = [
4
+ // High priority patterns (structural)
5
+ [
6
+ { regex: /\n\n/g }, // Double newline (paragraph break)
7
+ { regex: /\n/g }, // Single newline
8
+ { regex: /={3,}\s*\n|-{3,}\s*\n/g }, // Section separators
9
+ ],
10
+ // Medium priority (semantic)
11
+ [
12
+ { regex: /[.!?][")\]]?\s/g }, // End of sentence
13
+ { regex: /;\s/g }, // Semicolon
14
+ { regex: /:\s/g }, // Colon
15
+ ],
16
+ // Low priority (any breaks)
17
+ [
18
+ { regex: /,\s/g }, // Comma
19
+ { regex: /\s-\s/g }, // Dash surrounded by spaces
20
+ { regex: /\s/g }, // Any space
21
+ ],
22
+ ];
23
+ function findFirstMatch(text, regex) {
24
+ // Reset regex
25
+ regex.lastIndex = 0;
26
+ // For very long texts, try chunking
27
+ if (text.length > 10000) {
28
+ const chunkSize = 2000;
29
+ let position = 0;
30
+ while (position < text.length) {
31
+ const chunk = text.substring(position, position + chunkSize);
32
+ regex.lastIndex = 0;
33
+ const match = regex.exec(chunk);
34
+ if (match) {
35
+ return position + match.index;
36
+ }
37
+ // Move to next chunk with some overlap
38
+ position += chunkSize - 100;
39
+ if (position >= text.length)
40
+ break;
41
+ }
42
+ return -1;
43
+ }
44
+ // For shorter texts, normal regex search
45
+ const match = regex.exec(text);
46
+ return match ? match.index : -1;
47
+ }
48
+ // 3. Optimized boundary finding functions
49
+ function findLastMatch(text, regex) {
50
+ // Reset regex state
51
+ regex.lastIndex = 0;
52
+ let lastIndex = -1;
53
+ let lastLength = 0;
54
+ let match;
55
+ // For very long texts, use a different approach to avoid regex engine slowdowns
56
+ if (text.length > 10000) {
57
+ // Try dividing the text into chunks for faster processing
58
+ const chunkSize = 2000;
59
+ let startPosition = Math.max(0, text.length - chunkSize);
60
+ while (startPosition >= 0) {
61
+ const chunk = text.substring(startPosition, startPosition + chunkSize);
62
+ regex.lastIndex = 0;
63
+ let chunkLastIndex = -1;
64
+ let chunkLastLength = 0;
65
+ while ((match = regex.exec(chunk)) !== null) {
66
+ chunkLastIndex = match.index;
67
+ chunkLastLength = match[0].length;
68
+ }
69
+ if (chunkLastIndex !== -1) {
70
+ return startPosition + chunkLastIndex + chunkLastLength;
71
+ }
72
+ // Move to previous chunk with some overlap
73
+ startPosition = Math.max(0, startPosition - chunkSize + 100) - 1;
74
+ if (startPosition <= 0)
75
+ break;
76
+ }
77
+ return -1;
78
+ }
79
+ // For shorter texts, normal regex search
80
+ while ((match = regex.exec(text)) !== null) {
81
+ lastIndex = match.index;
82
+ lastLength = match[0].length;
83
+ }
84
+ return lastIndex === -1 ? -1 : lastIndex + lastLength;
85
+ }
86
+ // 4. Find the best boundary with priority groups
87
+ function findBestBoundary(text, direction = 'backward') {
88
+ if (!text || text.length === 0)
89
+ return 0;
90
+ // Try each priority group
91
+ for (const patternGroup of priorityPatterns) {
92
+ for (const pattern of patternGroup) {
93
+ const position = direction === 'backward'
94
+ ? findLastMatch(text, pattern.regex)
95
+ : findFirstMatch(text, pattern.regex);
96
+ if (position !== -1) {
97
+ return position;
98
+ }
99
+ }
100
+ }
101
+ // No match found, use character boundary
102
+ return direction === 'backward' ? text.length : 0;
103
+ }
104
+ /**
105
+ * Tracks references used in a highlight without changing their numbers
106
+ */
107
+ function trackReferencesInHighlight(text, sourceResult // Source containing the original references
108
+ ) {
109
+ // Track used references
110
+ const references = [];
111
+ if (!text || text.length === 0 || !text.includes('#')) {
112
+ return { references }; // Early return
113
+ }
114
+ // Quick check for reference markers
115
+ if (!text.includes('link#') &&
116
+ !text.includes('image#') &&
117
+ !text.includes('video#')) {
118
+ return { references };
119
+ }
120
+ // Get references from the source if available
121
+ const sourceRefs = sourceResult.references || {
122
+ links: [],
123
+ images: [],
124
+ videos: [],
125
+ };
126
+ // Find references but don't modify text
127
+ const refRegex = /\((link|image|video)#(\d+)(?:\s+"([^"]*)")?\)/g;
128
+ let match;
129
+ while ((match = refRegex.exec(text)) !== null) {
130
+ const [, type, indexStr] = match;
131
+ const originalIndex = parseInt(indexStr, 10) - 1; // Convert to 0-based
132
+ // Get the source array for this type
133
+ const refType = type;
134
+ const sourceArray = sourceRefs[`${refType}s`];
135
+ // Skip if invalid reference
136
+ if (!sourceArray ||
137
+ originalIndex < 0 ||
138
+ originalIndex >= sourceArray.length) {
139
+ continue; // Skip invalid references
140
+ }
141
+ // Get original reference
142
+ const reference = sourceArray[originalIndex];
143
+ // Track if not already tracked
144
+ const alreadyTracked = references.some((ref) => ref.type === refType && ref.originalIndex === originalIndex);
145
+ if (!alreadyTracked) {
146
+ references.push({
147
+ type: refType,
148
+ originalIndex,
149
+ reference,
150
+ });
151
+ }
152
+ }
153
+ return { references };
154
+ }
155
+ /**
156
+ * Expand highlights in search results using smart boundary detection.
157
+ *
158
+ * This implementation finds natural text boundaries like paragraphs, sentences,
159
+ * and phrases to provide context while maintaining readability.
160
+ *
161
+ * @param searchResults - Search results object
162
+ * @param mainExpandBy - Primary expansion size on each side (default: 300)
163
+ * @param separatorExpandBy - Additional range to look for separators (default: 150)
164
+ * @returns Copy of search results with expanded highlights and tracked references
165
+ */
166
+ function expandHighlights(searchResults, mainExpandBy = 300, separatorExpandBy = 150) {
167
+ // Avoid deep copy - only copy what we modify
168
+ const resultCopy = { ...searchResults };
169
+ if (resultCopy.organic)
170
+ resultCopy.organic = [...resultCopy.organic];
171
+ if (resultCopy.topStories)
172
+ resultCopy.topStories = [...resultCopy.topStories];
173
+ // Process the results efficiently
174
+ const processResultTypes = ['organic', 'topStories'];
175
+ for (const resultType of processResultTypes) {
176
+ if (!resultCopy[resultType])
177
+ continue;
178
+ // Map results to new array with modified highlights
179
+ resultCopy[resultType] = resultCopy[resultType]?.map((result) => {
180
+ if (result.content == null ||
181
+ result.content === '' ||
182
+ !result.highlights ||
183
+ result.highlights.length === 0) {
184
+ return result; // No modification needed
185
+ }
186
+ // Create a shallow copy with expanded highlights
187
+ const resultCopy = { ...result };
188
+ const content = result.content;
189
+ const highlights = [];
190
+ // Process each highlight
191
+ for (const highlight of result.highlights) {
192
+ const { references } = trackReferencesInHighlight(highlight.text, result);
193
+ let startPos = content.indexOf(highlight.text);
194
+ let highlightLen = highlight.text.length;
195
+ if (startPos === -1) {
196
+ // Try with stripped whitespace
197
+ const strippedHighlight = highlight.text.trim();
198
+ startPos = content.indexOf(strippedHighlight);
199
+ if (startPos === -1) {
200
+ highlights.push({
201
+ text: highlight.text,
202
+ score: highlight.score,
203
+ references,
204
+ });
205
+ continue;
206
+ }
207
+ highlightLen = strippedHighlight.length;
208
+ }
209
+ // Calculate boundaries
210
+ const mainStart = Math.max(0, startPos - mainExpandBy);
211
+ const mainEnd = Math.min(content.length, startPos + highlightLen + mainExpandBy);
212
+ const separatorStart = Math.max(0, mainStart - separatorExpandBy);
213
+ const separatorEnd = Math.min(content.length, mainEnd + separatorExpandBy);
214
+ // Extract text segments
215
+ const headText = content.substring(separatorStart, mainStart);
216
+ const tailText = content.substring(mainEnd, separatorEnd);
217
+ // Find natural boundaries
218
+ const bestHeadBoundary = findBestBoundary(headText, 'backward');
219
+ const bestTailBoundary = findBestBoundary(tailText, 'forward');
220
+ // Calculate final positions
221
+ const finalStart = separatorStart + bestHeadBoundary;
222
+ const finalEnd = mainEnd + bestTailBoundary;
223
+ // Extract the expanded highlight
224
+ const expandedHighlightText = content
225
+ .substring(finalStart, finalEnd)
226
+ .trim();
227
+ highlights.push({
228
+ text: expandedHighlightText,
229
+ score: highlight.score,
230
+ references,
231
+ });
232
+ }
233
+ resultCopy.highlights = highlights;
234
+ delete resultCopy.content;
235
+ delete resultCopy.references;
236
+ return resultCopy;
237
+ });
238
+ }
239
+ return resultCopy;
240
+ }
241
+
242
+ export { expandHighlights };
243
+ //# sourceMappingURL=highlights.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlights.mjs","sources":["../../../../src/tools/search/highlights.ts"],"sourcesContent":["import type * as t from './types';\n\n// 2. Pre-compile all regular expressions (only do this once)\n// Group patterns by priority for early returns\nconst priorityPatterns = [\n // High priority patterns (structural)\n [\n { regex: /\\n\\n/g }, // Double newline (paragraph break)\n { regex: /\\n/g }, // Single newline\n { regex: /={3,}\\s*\\n|-{3,}\\s*\\n/g }, // Section separators\n ],\n // Medium priority (semantic)\n [\n { regex: /[.!?][\")\\]]?\\s/g }, // End of sentence\n { regex: /;\\s/g }, // Semicolon\n { regex: /:\\s/g }, // Colon\n ],\n // Low priority (any breaks)\n [\n { regex: /,\\s/g }, // Comma\n { regex: /\\s-\\s/g }, // Dash surrounded by spaces\n { regex: /\\s/g }, // Any space\n ],\n];\n\nfunction findFirstMatch(text: string, regex: RegExp): number {\n // Reset regex\n regex.lastIndex = 0;\n\n // For very long texts, try chunking\n if (text.length > 10000) {\n const chunkSize = 2000;\n let position = 0;\n\n while (position < text.length) {\n const chunk = text.substring(position, position + chunkSize);\n regex.lastIndex = 0;\n\n const match = regex.exec(chunk);\n if (match) {\n return position + match.index;\n }\n\n // Move to next chunk with some overlap\n position += chunkSize - 100;\n if (position >= text.length) break;\n }\n return -1;\n }\n\n // For shorter texts, normal regex search\n const match = regex.exec(text);\n return match ? match.index : -1;\n}\n\n// 3. Optimized boundary finding functions\nfunction findLastMatch(text: string, regex: RegExp): number {\n // Reset regex state\n regex.lastIndex = 0;\n\n let lastIndex = -1;\n let lastLength = 0;\n let match;\n\n // For very long texts, use a different approach to avoid regex engine slowdowns\n if (text.length > 10000) {\n // Try dividing the text into chunks for faster processing\n const chunkSize = 2000;\n let startPosition = Math.max(0, text.length - chunkSize);\n\n while (startPosition >= 0) {\n const chunk = text.substring(startPosition, startPosition + chunkSize);\n regex.lastIndex = 0;\n\n let chunkLastIndex = -1;\n let chunkLastLength = 0;\n\n while ((match = regex.exec(chunk)) !== null) {\n chunkLastIndex = match.index;\n chunkLastLength = match[0].length;\n }\n\n if (chunkLastIndex !== -1) {\n return startPosition + chunkLastIndex + chunkLastLength;\n }\n\n // Move to previous chunk with some overlap\n startPosition = Math.max(0, startPosition - chunkSize + 100) - 1;\n if (startPosition <= 0) break;\n }\n return -1;\n }\n\n // For shorter texts, normal regex search\n while ((match = regex.exec(text)) !== null) {\n lastIndex = match.index;\n lastLength = match[0].length;\n }\n\n return lastIndex === -1 ? -1 : lastIndex + lastLength;\n}\n\n// 4. Find the best boundary with priority groups\nfunction findBestBoundary(text: string, direction = 'backward'): number {\n if (!text || text.length === 0) return 0;\n\n // Try each priority group\n for (const patternGroup of priorityPatterns) {\n for (const pattern of patternGroup) {\n const position =\n direction === 'backward'\n ? findLastMatch(text, pattern.regex)\n : findFirstMatch(text, pattern.regex);\n\n if (position !== -1) {\n return position;\n }\n }\n }\n\n // No match found, use character boundary\n return direction === 'backward' ? text.length : 0;\n}\n\n/**\n * Tracks references used in a highlight without changing their numbers\n */\nfunction trackReferencesInHighlight(\n text: string,\n sourceResult: t.ValidSource // Source containing the original references\n): {\n references: {\n type: 'link' | 'image' | 'video';\n originalIndex: number;\n reference: t.MediaReference; // Original reference object\n }[];\n} {\n // Track used references\n const references: {\n type: 'link' | 'image' | 'video';\n originalIndex: number;\n reference: t.MediaReference;\n }[] = [];\n\n if (!text || text.length === 0 || !text.includes('#')) {\n return { references }; // Early return\n }\n\n // Quick check for reference markers\n if (\n !text.includes('link#') &&\n !text.includes('image#') &&\n !text.includes('video#')\n ) {\n return { references };\n }\n\n // Get references from the source if available\n const sourceRefs = sourceResult.references || {\n links: [],\n images: [],\n videos: [],\n };\n\n // Find references but don't modify text\n const refRegex = /\\((link|image|video)#(\\d+)(?:\\s+\"([^\"]*)\")?\\)/g;\n let match;\n\n while ((match = refRegex.exec(text)) !== null) {\n const [, type, indexStr] = match;\n const originalIndex = parseInt(indexStr, 10) - 1; // Convert to 0-based\n\n // Get the source array for this type\n const refType = type as 'link' | 'image' | 'video';\n const sourceArray = sourceRefs[`${refType}s`] as\n | t.MediaReference[]\n | undefined;\n\n // Skip if invalid reference\n if (\n !sourceArray ||\n originalIndex < 0 ||\n originalIndex >= sourceArray.length\n ) {\n continue; // Skip invalid references\n }\n\n // Get original reference\n const reference = sourceArray[originalIndex];\n\n // Track if not already tracked\n const alreadyTracked = references.some(\n (ref) => ref.type === refType && ref.originalIndex === originalIndex\n );\n\n if (!alreadyTracked) {\n references.push({\n type: refType,\n originalIndex,\n reference,\n });\n }\n }\n\n return { references };\n}\n\n/**\n * Expand highlights in search results using smart boundary detection.\n *\n * This implementation finds natural text boundaries like paragraphs, sentences,\n * and phrases to provide context while maintaining readability.\n *\n * @param searchResults - Search results object\n * @param mainExpandBy - Primary expansion size on each side (default: 300)\n * @param separatorExpandBy - Additional range to look for separators (default: 150)\n * @returns Copy of search results with expanded highlights and tracked references\n */\nexport function expandHighlights(\n searchResults: t.SearchResultData,\n mainExpandBy = 300,\n separatorExpandBy = 150\n): t.SearchResultData {\n // Avoid deep copy - only copy what we modify\n const resultCopy = { ...searchResults };\n if (resultCopy.organic) resultCopy.organic = [...resultCopy.organic];\n if (resultCopy.topStories) resultCopy.topStories = [...resultCopy.topStories];\n\n // Process the results efficiently\n const processResultTypes = ['organic', 'topStories'] as const;\n\n for (const resultType of processResultTypes) {\n if (!resultCopy[resultType as 'organic' | 'topStories']) continue;\n\n // Map results to new array with modified highlights\n resultCopy[resultType] = resultCopy[resultType]?.map((result) => {\n if (\n result.content == null ||\n result.content === '' ||\n !result.highlights ||\n result.highlights.length === 0\n ) {\n return result; // No modification needed\n }\n\n // Create a shallow copy with expanded highlights\n const resultCopy = { ...result };\n const content = result.content;\n const highlights = [];\n // Process each highlight\n for (const highlight of result.highlights) {\n const { references } = trackReferencesInHighlight(\n highlight.text,\n result\n );\n\n let startPos = content.indexOf(highlight.text);\n let highlightLen = highlight.text.length;\n\n if (startPos === -1) {\n // Try with stripped whitespace\n const strippedHighlight = highlight.text.trim();\n startPos = content.indexOf(strippedHighlight);\n\n if (startPos === -1) {\n highlights.push({\n text: highlight.text,\n score: highlight.score,\n references,\n });\n continue;\n }\n highlightLen = strippedHighlight.length;\n }\n\n // Calculate boundaries\n const mainStart = Math.max(0, startPos - mainExpandBy);\n const mainEnd = Math.min(\n content.length,\n startPos + highlightLen + mainExpandBy\n );\n\n const separatorStart = Math.max(0, mainStart - separatorExpandBy);\n const separatorEnd = Math.min(\n content.length,\n mainEnd + separatorExpandBy\n );\n\n // Extract text segments\n const headText = content.substring(separatorStart, mainStart);\n const tailText = content.substring(mainEnd, separatorEnd);\n\n // Find natural boundaries\n const bestHeadBoundary = findBestBoundary(headText, 'backward');\n const bestTailBoundary = findBestBoundary(tailText, 'forward');\n\n // Calculate final positions\n const finalStart = separatorStart + bestHeadBoundary;\n const finalEnd = mainEnd + bestTailBoundary;\n\n // Extract the expanded highlight\n const expandedHighlightText = content\n .substring(finalStart, finalEnd)\n .trim();\n highlights.push({\n text: expandedHighlightText,\n score: highlight.score,\n references,\n });\n }\n\n resultCopy.highlights = highlights;\n delete resultCopy.content;\n delete resultCopy.references;\n return resultCopy;\n });\n }\n\n return resultCopy;\n}\n"],"names":[],"mappings":"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"}