@librechat/agents 2.4.321 → 2.4.322

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 (45) hide show
  1. package/dist/cjs/tools/search/firecrawl.cjs +6 -4
  2. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  3. package/dist/cjs/tools/search/format.cjs +6 -0
  4. package/dist/cjs/tools/search/format.cjs.map +1 -1
  5. package/dist/cjs/tools/search/rerankers.cjs +43 -36
  6. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  7. package/dist/cjs/tools/search/schema.cjs +70 -0
  8. package/dist/cjs/tools/search/schema.cjs.map +1 -0
  9. package/dist/cjs/tools/search/search.cjs +62 -25
  10. package/dist/cjs/tools/search/search.cjs.map +1 -1
  11. package/dist/cjs/tools/search/tool.cjs +162 -47
  12. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  13. package/dist/cjs/tools/search/utils.cjs +34 -5
  14. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  15. package/dist/esm/tools/search/firecrawl.mjs +6 -4
  16. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  17. package/dist/esm/tools/search/format.mjs +7 -1
  18. package/dist/esm/tools/search/format.mjs.map +1 -1
  19. package/dist/esm/tools/search/rerankers.mjs +43 -36
  20. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  21. package/dist/esm/tools/search/schema.mjs +61 -0
  22. package/dist/esm/tools/search/schema.mjs.map +1 -0
  23. package/dist/esm/tools/search/search.mjs +63 -26
  24. package/dist/esm/tools/search/search.mjs.map +1 -1
  25. package/dist/esm/tools/search/tool.mjs +161 -46
  26. package/dist/esm/tools/search/tool.mjs.map +1 -1
  27. package/dist/esm/tools/search/utils.mjs +33 -6
  28. package/dist/esm/tools/search/utils.mjs.map +1 -1
  29. package/dist/types/tools/search/firecrawl.d.ts +1 -0
  30. package/dist/types/tools/search/rerankers.d.ts +8 -4
  31. package/dist/types/tools/search/schema.d.ts +16 -0
  32. package/dist/types/tools/search/tool.d.ts +13 -0
  33. package/dist/types/tools/search/types.d.ts +34 -0
  34. package/dist/types/tools/search/utils.d.ts +9 -2
  35. package/package.json +3 -2
  36. package/src/scripts/search.ts +3 -3
  37. package/src/tools/search/firecrawl.ts +9 -4
  38. package/src/tools/search/format.ts +8 -1
  39. package/src/tools/search/rerankers.ts +57 -36
  40. package/src/tools/search/schema.ts +63 -0
  41. package/src/tools/search/search.ts +74 -22
  42. package/src/tools/search/tool.ts +217 -44
  43. package/src/tools/search/types.ts +35 -0
  44. package/src/tools/search/utils.ts +37 -5
  45. package/src/utils/llmConfig.ts +1 -1
@@ -0,0 +1,16 @@
1
+ import { z } from 'zod';
2
+ export declare enum DATE_RANGE {
3
+ PAST_HOUR = "h",
4
+ PAST_24_HOURS = "d",
5
+ PAST_WEEK = "w",
6
+ PAST_MONTH = "m",
7
+ PAST_YEAR = "y"
8
+ }
9
+ export declare const DEFAULT_QUERY_DESCRIPTION: string;
10
+ export declare const DEFAULT_COUNTRY_DESCRIPTION: string;
11
+ export declare const querySchema: z.ZodString;
12
+ export declare const dateSchema: z.ZodOptional<z.ZodNativeEnum<typeof DATE_RANGE>>;
13
+ export declare const countrySchema: z.ZodOptional<z.ZodString>;
14
+ export declare const imagesSchema: z.ZodOptional<z.ZodBoolean>;
15
+ export declare const videosSchema: z.ZodOptional<z.ZodBoolean>;
16
+ export declare const newsSchema: z.ZodOptional<z.ZodBoolean>;
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { DynamicStructuredTool } from '@langchain/core/tools';
3
3
  import type * as t from './types';
4
+ import { DATE_RANGE } from './schema';
4
5
  /**
5
6
  * Creates a search tool with a schema that dynamically includes the country field
6
7
  * only when the searchProvider is 'serper'.
@@ -10,11 +11,23 @@ import type * as t from './types';
10
11
  */
11
12
  export declare const createSearchTool: (config?: t.SearchToolConfig) => DynamicStructuredTool<z.ZodObject<{
12
13
  query: z.ZodString;
14
+ date: z.ZodOptional<z.ZodNativeEnum<typeof DATE_RANGE>>;
13
15
  country?: z.ZodOptional<z.ZodString>;
16
+ images: z.ZodOptional<z.ZodBoolean>;
17
+ videos: z.ZodOptional<z.ZodBoolean>;
18
+ news: z.ZodOptional<z.ZodBoolean>;
14
19
  }, "strip", z.ZodTypeAny, {
15
20
  query: string;
21
+ date?: DATE_RANGE | undefined;
22
+ images?: boolean | undefined;
23
+ videos?: boolean | undefined;
24
+ news?: boolean | undefined;
16
25
  country?: unknown;
17
26
  }, {
18
27
  query: string;
28
+ date?: DATE_RANGE | undefined;
29
+ images?: boolean | undefined;
30
+ videos?: boolean | undefined;
31
+ news?: boolean | undefined;
19
32
  country?: unknown;
20
33
  }>>;
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod';
2
+ import type { Logger as WinstonLogger } from 'winston';
2
3
  import type { RunnableConfig } from '@langchain/core/runnables';
3
4
  import type { BaseReranker } from './rerankers';
5
+ import { DATE_RANGE } from './schema';
4
6
  export type SearchProvider = 'serper' | 'searxng';
5
7
  export type RerankerType = 'infinity' | 'jina' | 'cohere' | 'none';
6
8
  export interface Highlight {
@@ -78,6 +80,7 @@ export interface ProcessSourcesConfig {
78
80
  strategies?: string[];
79
81
  filterContent?: boolean;
80
82
  reranker?: BaseReranker;
83
+ logger?: Logger;
81
84
  }
82
85
  export interface FirecrawlConfig {
83
86
  firecrawlApiKey?: string;
@@ -121,7 +124,11 @@ export interface CohereRerankerResponse {
121
124
  };
122
125
  };
123
126
  }
127
+ export type SafeSearchLevel = 0 | 1 | 2;
128
+ export type Logger = WinstonLogger;
124
129
  export interface SearchToolConfig extends SearchConfig, ProcessSourcesConfig, FirecrawlConfig {
130
+ logger?: Logger;
131
+ safeSearch?: SafeSearchLevel;
125
132
  jinaApiKey?: string;
126
133
  cohereApiKey?: string;
127
134
  rerankerType?: RerankerType;
@@ -217,11 +224,18 @@ export interface FirecrawlScraperConfig {
217
224
  apiUrl?: string;
218
225
  formats?: string[];
219
226
  timeout?: number;
227
+ logger?: Logger;
220
228
  }
221
229
  export type GetSourcesParams = {
222
230
  query: string;
231
+ date?: DATE_RANGE;
223
232
  country?: string;
224
233
  numResults?: number;
234
+ safeSearch?: SearchToolConfig['safeSearch'];
235
+ images?: boolean;
236
+ videos?: boolean;
237
+ news?: boolean;
238
+ type?: 'search' | 'images' | 'videos' | 'news';
225
239
  };
226
240
  /** Serper API */
227
241
  export interface VideoResult {
@@ -382,6 +396,13 @@ export interface SerperSearchInput {
382
396
  */
383
397
  autocorrect?: boolean;
384
398
  page?: number;
399
+ /**
400
+ * Date range for search results
401
+ * Options: "h" (past hour), "d" (past 24 hours), "w" (past week),
402
+ * "m" (past month), "y" (past year)
403
+ * `qdr:${DATE_RANGE}`
404
+ */
405
+ tbs?: string;
385
406
  }
386
407
  export type SerperResultData = {
387
408
  searchParameters: SerperSearchPayload;
@@ -491,16 +512,29 @@ export type ProcessSourcesFields = {
491
512
  result: SearchResult;
492
513
  numElements: number;
493
514
  query: string;
515
+ news: boolean;
494
516
  proMode: boolean;
495
517
  onGetHighlights: SearchToolConfig['onGetHighlights'];
496
518
  };
497
519
  export type SearchToolSchema = z.ZodObject<{
498
520
  query: z.ZodString;
521
+ date: z.ZodOptional<z.ZodNativeEnum<typeof DATE_RANGE>>;
499
522
  country?: z.ZodOptional<z.ZodString>;
523
+ images: z.ZodOptional<z.ZodBoolean>;
524
+ videos: z.ZodOptional<z.ZodBoolean>;
525
+ news: z.ZodOptional<z.ZodBoolean>;
500
526
  }, 'strip', z.ZodTypeAny, {
501
527
  query: string;
528
+ date?: DATE_RANGE;
502
529
  country?: unknown;
530
+ images?: boolean;
531
+ videos?: boolean;
532
+ news?: boolean;
503
533
  }, {
504
534
  query: string;
535
+ date?: DATE_RANGE;
505
536
  country?: unknown;
537
+ images?: boolean;
538
+ videos?: boolean;
539
+ news?: boolean;
506
540
  }>;
@@ -1,3 +1,10 @@
1
1
  import type * as t from './types';
2
- export declare const getDomainName: (link: string, metadata?: t.ScrapeMetadata) => string | undefined;
3
- export declare function getAttribution(link: string, metadata?: t.ScrapeMetadata): string | undefined;
2
+ /**
3
+ * Creates a default logger that maps to console methods
4
+ * Uses a singleton pattern to avoid creating multiple instances
5
+ * @returns A default logger that implements the Logger interface
6
+ */
7
+ export declare const createDefaultLogger: () => t.Logger;
8
+ export declare const fileExtRegex: RegExp;
9
+ export declare const getDomainName: (link: string, metadata?: t.ScrapeMetadata, logger?: t.Logger) => string | undefined;
10
+ export declare function getAttribution(link: string, metadata?: t.ScrapeMetadata, logger?: t.Logger): string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "2.4.321",
3
+ "version": "2.4.322",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -122,7 +122,8 @@
122
122
  "tsc-alias": "^1.8.10",
123
123
  "tsconfig-paths": "^4.2.0",
124
124
  "tslib": "^2.6.3",
125
- "typescript": "^5.5.3"
125
+ "typescript": "^5.5.3",
126
+ "winston": "^3.17.0"
126
127
  },
127
128
  "lint-staged": {
128
129
  "*.{js,ts}": [
@@ -111,10 +111,10 @@ async function testStandardStreaming(): Promise<void> {
111
111
  // `;
112
112
  // const userMessage = 'Are massage guns good?';
113
113
  // const userMessage = 'What is functional programming?';
114
- // const userMessage = "Get me today's trending news.";
114
+ const userMessage = "Get me today's trending news.";
115
115
  // const userMessage = "search recent italy earthquake volcano activity";
116
- const userMessage =
117
- "use 'Trump' as the exact search query and tell me what you find.";
116
+ // const userMessage =
117
+ // "use 'Trump' as the exact search query and tell me what you find.";
118
118
 
119
119
  conversationHistory.push(new HumanMessage(userMessage));
120
120
 
@@ -1,7 +1,7 @@
1
- /* eslint-disable no-console */
2
1
  import axios from 'axios';
3
2
  import { processContent } from './content';
4
3
  import type * as t from './types';
4
+ import { createDefaultLogger } from './utils';
5
5
 
6
6
  /**
7
7
  * Firecrawl scraper implementation
@@ -12,6 +12,7 @@ export class FirecrawlScraper {
12
12
  private apiUrl: string;
13
13
  private defaultFormats: string[];
14
14
  private timeout: number;
15
+ private logger: t.Logger;
15
16
 
16
17
  constructor(config: t.FirecrawlScraperConfig = {}) {
17
18
  this.apiKey = config.apiKey ?? process.env.FIRECRAWL_API_KEY ?? '';
@@ -25,11 +26,15 @@ export class FirecrawlScraper {
25
26
  this.defaultFormats = config.formats ?? ['markdown', 'html'];
26
27
  this.timeout = config.timeout ?? 15000;
27
28
 
29
+ this.logger = config.logger || createDefaultLogger();
30
+
28
31
  if (!this.apiKey) {
29
- console.warn('FIRECRAWL_API_KEY is not set. Scraping will not work.');
32
+ this.logger.warn('FIRECRAWL_API_KEY is not set. Scraping will not work.');
30
33
  }
31
34
 
32
- console.log(`Firecrawl scraper initialized with API URL: ${this.apiUrl}`);
35
+ this.logger.debug(
36
+ `Firecrawl scraper initialized with API URL: ${this.apiUrl}`
37
+ );
33
38
  }
34
39
 
35
40
  /**
@@ -107,7 +112,7 @@ export class FirecrawlScraper {
107
112
  );
108
113
  return [markdown, rest];
109
114
  } catch (error) {
110
- console.error('Error processing content:', error);
115
+ this.logger.error('Error processing content:', error);
111
116
  return [response.data.markdown, undefined];
112
117
  }
113
118
  } else if (response.data.markdown != null) {
@@ -1,5 +1,5 @@
1
1
  import type * as t from './types';
2
- import { getDomainName } from './utils';
2
+ import { getDomainName, fileExtRegex } from './utils';
3
3
 
4
4
  function addHighlightSection(): string[] {
5
5
  return ['\n## Highlights', ''];
@@ -62,6 +62,9 @@ function formatSource(
62
62
 
63
63
  for (let j = 0; j < h.references.length; j++) {
64
64
  const ref = h.references[j];
65
+ if (ref.reference.originalUrl.includes('mailto:')) {
66
+ continue;
67
+ }
65
68
  references.push({
66
69
  type: ref.type,
67
70
  link: ref.reference.originalUrl,
@@ -76,6 +79,10 @@ function formatSource(
76
79
  continue;
77
80
  }
78
81
 
82
+ if (fileExtRegex.test(ref.reference.originalUrl)) {
83
+ continue;
84
+ }
85
+
79
86
  if (!hasHeader) {
80
87
  refLines.push('Core References:');
81
88
  hasHeader = true;
@@ -1,12 +1,14 @@
1
- /* eslint-disable no-console */
2
1
  import axios from 'axios';
3
2
  import type * as t from './types';
3
+ import { createDefaultLogger } from './utils';
4
4
 
5
5
  export abstract class BaseReranker {
6
6
  protected apiKey: string | undefined;
7
+ protected logger: t.Logger;
7
8
 
8
- constructor() {
9
+ constructor(logger?: t.Logger) {
9
10
  // Each specific reranker will set its API key
11
+ this.logger = logger || createDefaultLogger();
10
12
  }
11
13
 
12
14
  abstract rerank(
@@ -25,16 +27,22 @@ export abstract class BaseReranker {
25
27
  }
26
28
 
27
29
  protected logDocumentSamples(documents: string[]): void {
28
- console.log('Sample documents being sent to API:');
30
+ this.logger.debug('Sample documents being sent to API:');
29
31
  for (let i = 0; i < Math.min(3, documents.length); i++) {
30
- console.log(`Document ${i}: ${documents[i].substring(0, 100)}...`);
32
+ this.logger.debug(`Document ${i}: ${documents[i].substring(0, 100)}...`);
31
33
  }
32
34
  }
33
35
  }
34
36
 
35
37
  export class JinaReranker extends BaseReranker {
36
- constructor({ apiKey = process.env.JINA_API_KEY }: { apiKey?: string }) {
37
- super();
38
+ constructor({
39
+ apiKey = process.env.JINA_API_KEY,
40
+ logger,
41
+ }: {
42
+ apiKey?: string;
43
+ logger?: t.Logger;
44
+ }) {
45
+ super(logger);
38
46
  this.apiKey = apiKey;
39
47
  }
40
48
 
@@ -43,11 +51,11 @@ export class JinaReranker extends BaseReranker {
43
51
  documents: string[],
44
52
  topK: number = 5
45
53
  ): Promise<t.Highlight[]> {
46
- console.log(`Reranking ${documents.length} documents with Jina`);
54
+ this.logger.debug(`Reranking ${documents.length} documents with Jina`);
47
55
 
48
56
  try {
49
57
  if (this.apiKey == null || this.apiKey === '') {
50
- console.warn('JINA_API_KEY is not set. Using default ranking.');
58
+ this.logger.warn('JINA_API_KEY is not set. Using default ranking.');
51
59
  return this.getDefaultRanking(documents, topK);
52
60
  }
53
61
 
@@ -73,14 +81,14 @@ export class JinaReranker extends BaseReranker {
73
81
  );
74
82
 
75
83
  // Log the response data structure
76
- console.log('Jina API response structure:');
77
- console.log('Model:', response.data?.model);
78
- console.log('Usage:', response.data?.usage);
79
- console.log('Results count:', response.data?.results.length);
84
+ this.logger.debug('Jina API response structure:');
85
+ this.logger.debug('Model:', response.data?.model);
86
+ this.logger.debug('Usage:', response.data?.usage);
87
+ this.logger.debug('Results count:', response.data?.results.length);
80
88
 
81
89
  // Log a sample of the results
82
90
  if ((response.data?.results.length ?? 0) > 0) {
83
- console.log(
91
+ this.logger.debug(
84
92
  'Sample result:',
85
93
  JSON.stringify(response.data?.results[0], null, 2)
86
94
  );
@@ -108,13 +116,13 @@ export class JinaReranker extends BaseReranker {
108
116
  return { text, score };
109
117
  });
110
118
  } else {
111
- console.warn(
119
+ this.logger.warn(
112
120
  'Unexpected response format from Jina API. Using default ranking.'
113
121
  );
114
122
  return this.getDefaultRanking(documents, topK);
115
123
  }
116
124
  } catch (error) {
117
- console.error('Error using Jina reranker:', error);
125
+ this.logger.error('Error using Jina reranker:', error);
118
126
  // Fallback to default ranking on error
119
127
  return this.getDefaultRanking(documents, topK);
120
128
  }
@@ -122,8 +130,14 @@ export class JinaReranker extends BaseReranker {
122
130
  }
123
131
 
124
132
  export class CohereReranker extends BaseReranker {
125
- constructor({ apiKey = process.env.COHERE_API_KEY }: { apiKey?: string }) {
126
- super();
133
+ constructor({
134
+ apiKey = process.env.COHERE_API_KEY,
135
+ logger,
136
+ }: {
137
+ apiKey?: string;
138
+ logger?: t.Logger;
139
+ }) {
140
+ super(logger);
127
141
  this.apiKey = apiKey;
128
142
  }
129
143
 
@@ -132,11 +146,11 @@ export class CohereReranker extends BaseReranker {
132
146
  documents: string[],
133
147
  topK: number = 5
134
148
  ): Promise<t.Highlight[]> {
135
- console.log(`Reranking ${documents.length} documents with Cohere`);
149
+ this.logger.debug(`Reranking ${documents.length} documents with Cohere`);
136
150
 
137
151
  try {
138
152
  if (this.apiKey == null || this.apiKey === '') {
139
- console.warn('COHERE_API_KEY is not set. Using default ranking.');
153
+ this.logger.warn('COHERE_API_KEY is not set. Using default ranking.');
140
154
  return this.getDefaultRanking(documents, topK);
141
155
  }
142
156
 
@@ -161,14 +175,14 @@ export class CohereReranker extends BaseReranker {
161
175
  );
162
176
 
163
177
  // Log the response data structure
164
- console.log('Cohere API response structure:');
165
- console.log('ID:', response.data?.id);
166
- console.log('Meta:', response.data?.meta);
167
- console.log('Results count:', response.data?.results.length);
178
+ this.logger.debug('Cohere API response structure:');
179
+ this.logger.debug('ID:', response.data?.id);
180
+ this.logger.debug('Meta:', response.data?.meta);
181
+ this.logger.debug('Results count:', response.data?.results.length);
168
182
 
169
183
  // Log a sample of the results
170
184
  if ((response.data?.results.length ?? 0) > 0) {
171
- console.log(
185
+ this.logger.debug(
172
186
  'Sample result:',
173
187
  JSON.stringify(response.data?.results[0], null, 2)
174
188
  );
@@ -182,13 +196,13 @@ export class CohereReranker extends BaseReranker {
182
196
  return { text, score };
183
197
  });
184
198
  } else {
185
- console.warn(
199
+ this.logger.warn(
186
200
  'Unexpected response format from Cohere API. Using default ranking.'
187
201
  );
188
202
  return this.getDefaultRanking(documents, topK);
189
203
  }
190
204
  } catch (error) {
191
- console.error('Error using Cohere reranker:', error);
205
+ this.logger.error('Error using Cohere reranker:', error);
192
206
  // Fallback to default ranking on error
193
207
  return this.getDefaultRanking(documents, topK);
194
208
  }
@@ -196,8 +210,8 @@ export class CohereReranker extends BaseReranker {
196
210
  }
197
211
 
198
212
  export class InfinityReranker extends BaseReranker {
199
- constructor() {
200
- super();
213
+ constructor(logger?: t.Logger) {
214
+ super(logger);
201
215
  // No API key needed for the placeholder implementation
202
216
  }
203
217
 
@@ -206,7 +220,7 @@ export class InfinityReranker extends BaseReranker {
206
220
  documents: string[],
207
221
  topK: number = 5
208
222
  ): Promise<t.Highlight[]> {
209
- console.log(
223
+ this.logger.debug(
210
224
  `Reranking ${documents.length} documents with Infinity (placeholder)`
211
225
  );
212
226
  // This would be replaced with actual Infinity reranker implementation
@@ -221,24 +235,31 @@ export const createReranker = (config: {
221
235
  rerankerType: t.RerankerType;
222
236
  jinaApiKey?: string;
223
237
  cohereApiKey?: string;
238
+ logger?: t.Logger;
224
239
  }): BaseReranker | undefined => {
225
- const { rerankerType, jinaApiKey, cohereApiKey } = config;
240
+ const { rerankerType, jinaApiKey, cohereApiKey, logger } = config;
241
+
242
+ // Create a default logger if none is provided
243
+ const defaultLogger = logger || createDefaultLogger();
226
244
 
227
245
  switch (rerankerType.toLowerCase()) {
228
246
  case 'jina':
229
- return new JinaReranker({ apiKey: jinaApiKey });
247
+ return new JinaReranker({ apiKey: jinaApiKey, logger: defaultLogger });
230
248
  case 'cohere':
231
- return new CohereReranker({ apiKey: cohereApiKey });
249
+ return new CohereReranker({
250
+ apiKey: cohereApiKey,
251
+ logger: defaultLogger,
252
+ });
232
253
  case 'infinity':
233
- return new InfinityReranker();
254
+ return new InfinityReranker(defaultLogger);
234
255
  case 'none':
235
- console.log('Skipping reranking as reranker is set to "none"');
256
+ defaultLogger.debug('Skipping reranking as reranker is set to "none"');
236
257
  return undefined;
237
258
  default:
238
- console.warn(
259
+ defaultLogger.warn(
239
260
  `Unknown reranker type: ${rerankerType}. Defaulting to InfinityReranker.`
240
261
  );
241
- return new JinaReranker({ apiKey: jinaApiKey });
262
+ return new JinaReranker({ apiKey: jinaApiKey, logger: defaultLogger });
242
263
  }
243
264
  };
244
265
 
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+
3
+ export enum DATE_RANGE {
4
+ PAST_HOUR = 'h',
5
+ PAST_24_HOURS = 'd',
6
+ PAST_WEEK = 'w',
7
+ PAST_MONTH = 'm',
8
+ PAST_YEAR = 'y',
9
+ }
10
+
11
+ export const DEFAULT_QUERY_DESCRIPTION = `
12
+ GUIDELINES:
13
+ - Start broad, then narrow: Begin with key concepts, then refine with specifics
14
+ - Think like sources: Use terminology experts would use in the field
15
+ - Consider perspective: Frame queries from different viewpoints for better results
16
+ - Quality over quantity: A precise 3-4 word query often beats lengthy sentences
17
+
18
+ TECHNIQUES (combine for power searches):
19
+ - EXACT PHRASES: Use quotes ("climate change report")
20
+ - EXCLUDE TERMS: Use minus to remove unwanted results (-wikipedia)
21
+ - SITE-SPECIFIC: Restrict to websites (site:edu research)
22
+ - FILETYPE: Find specific documents (filetype:pdf study)
23
+ - OR OPERATOR: Find alternatives (electric OR hybrid cars)
24
+ - DATE RANGE: Recent information (data after:2020)
25
+ - WILDCARDS: Use * for unknown terms (how to * bread)
26
+ - SPECIFIC QUESTIONS: Use who/what/when/where/why/how
27
+ - DOMAIN TERMS: Include technical terminology for specialized topics
28
+ - CONCISE TERMS: Prioritize keywords over sentences
29
+ `.trim();
30
+
31
+ export const DEFAULT_COUNTRY_DESCRIPTION =
32
+ `Country code to localize search results.
33
+ Use standard 2-letter country codes: "us", "uk", "ca", "de", "fr", "jp", "br", etc.
34
+ Provide this when the search should return results specific to a particular country.
35
+ Examples:
36
+ - "us" for United States (default)
37
+ - "de" for Germany
38
+ - "in" for India
39
+ `.trim();
40
+
41
+ export const querySchema = z.string().describe(DEFAULT_QUERY_DESCRIPTION);
42
+ export const dateSchema = z
43
+ .nativeEnum(DATE_RANGE)
44
+ .optional()
45
+ .describe('Date range for search results.');
46
+ export const countrySchema = z
47
+ .string()
48
+ .optional()
49
+ .describe(DEFAULT_COUNTRY_DESCRIPTION);
50
+ export const imagesSchema = z
51
+ .boolean()
52
+ .optional()
53
+ .describe('Whether to also run an image search.');
54
+
55
+ export const videosSchema = z
56
+ .boolean()
57
+ .optional()
58
+ .describe('Whether to also run a video search.');
59
+
60
+ export const newsSchema = z
61
+ .boolean()
62
+ .optional()
63
+ .describe('Whether to also run a news search.');