@memberjunction/web-search-engine 0.0.1-seed.1 → 6.2.0-edge.0

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 (47) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +343 -3
  3. package/dist/BaseWebSearchProvider.d.ts +89 -0
  4. package/dist/BaseWebSearchProvider.d.ts.map +1 -0
  5. package/dist/BaseWebSearchProvider.js +130 -0
  6. package/dist/BaseWebSearchProvider.js.map +1 -0
  7. package/dist/WebSearchEngine.d.ts +107 -0
  8. package/dist/WebSearchEngine.d.ts.map +1 -0
  9. package/dist/WebSearchEngine.js +396 -0
  10. package/dist/WebSearchEngine.js.map +1 -0
  11. package/dist/index.d.ts +28 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +43 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/operations/WebSearchQueryOperation.d.ts +8 -0
  16. package/dist/operations/WebSearchQueryOperation.d.ts.map +1 -0
  17. package/dist/operations/WebSearchQueryOperation.js +72 -0
  18. package/dist/operations/WebSearchQueryOperation.js.map +1 -0
  19. package/dist/providers/BraveWebSearchProvider.d.ts +41 -0
  20. package/dist/providers/BraveWebSearchProvider.d.ts.map +1 -0
  21. package/dist/providers/BraveWebSearchProvider.js +169 -0
  22. package/dist/providers/BraveWebSearchProvider.js.map +1 -0
  23. package/dist/providers/DuckDuckGoWebSearchProvider.d.ts +45 -0
  24. package/dist/providers/DuckDuckGoWebSearchProvider.d.ts.map +1 -0
  25. package/dist/providers/DuckDuckGoWebSearchProvider.js +116 -0
  26. package/dist/providers/DuckDuckGoWebSearchProvider.js.map +1 -0
  27. package/dist/providers/GoogleCustomSearchWebSearchProvider.d.ts +36 -0
  28. package/dist/providers/GoogleCustomSearchWebSearchProvider.d.ts.map +1 -0
  29. package/dist/providers/GoogleCustomSearchWebSearchProvider.js +125 -0
  30. package/dist/providers/GoogleCustomSearchWebSearchProvider.js.map +1 -0
  31. package/dist/providers/PerplexityWebSearchProvider.d.ts +40 -0
  32. package/dist/providers/PerplexityWebSearchProvider.d.ts.map +1 -0
  33. package/dist/providers/PerplexityWebSearchProvider.js +148 -0
  34. package/dist/providers/PerplexityWebSearchProvider.js.map +1 -0
  35. package/dist/providers/TavilyWebSearchProvider.d.ts +30 -0
  36. package/dist/providers/TavilyWebSearchProvider.d.ts.map +1 -0
  37. package/dist/providers/TavilyWebSearchProvider.js +126 -0
  38. package/dist/providers/TavilyWebSearchProvider.js.map +1 -0
  39. package/dist/providers/httpFailure.d.ts +29 -0
  40. package/dist/providers/httpFailure.d.ts.map +1 -0
  41. package/dist/providers/httpFailure.js +136 -0
  42. package/dist/providers/httpFailure.js.map +1 -0
  43. package/dist/types.d.ts +214 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +17 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +28 -5
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @fileoverview Google Custom Search driver — retiring 2027-01-01.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var GoogleCustomSearchWebSearchProvider_1;
12
+ import { RegisterClass } from '@memberjunction/global';
13
+ import { HttpGet } from '@memberjunction/network-utils';
14
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
15
+ import { classifyHttpFailure, failure } from './httpFailure.js';
16
+ /**
17
+ * Searches through the Google Custom Search JSON API.
18
+ *
19
+ * **This provider has an end date.** Google closed the API to new customers during 2025 and
20
+ * discontinues it on **2027-01-01**. It ships so that hosts already holding a key keep working
21
+ * through the transition, and it is ordered behind Brave and Tavily deliberately.
22
+ *
23
+ * It needs no dated code change to retire: once Google stops serving, calls fail, the engine
24
+ * fails over, and removing the metadata row takes it out of rotation entirely. A host that
25
+ * never had a key sees `CheckAvailability` self-disable it on the first config, which is the
26
+ * ordinary "not configured here" path rather than an error.
27
+ *
28
+ * Credential keys `apiKey` and `cx`; environment fallbacks `GOOGLE_CUSTOM_SEARCH_API_KEY`
29
+ * and `GOOGLE_CUSTOM_SEARCH_CX`.
30
+ */
31
+ let GoogleCustomSearchWebSearchProvider = class GoogleCustomSearchWebSearchProvider extends BaseWebSearchProvider {
32
+ constructor() {
33
+ super(...arguments);
34
+ this.Capabilities = {
35
+ Answer: false,
36
+ DomainFilter: true,
37
+ Freshness: true,
38
+ Region: true,
39
+ // Google serves at most 10 per request and indexes only the first 100 results.
40
+ MaxResultsCap: 10,
41
+ };
42
+ }
43
+ static { GoogleCustomSearchWebSearchProvider_1 = this; }
44
+ static { this.ENDPOINT = 'https://www.googleapis.com/customsearch/v1'; }
45
+ async CheckAvailability(_contextUser) {
46
+ this.apiKey = this.GetSecret('apiKey', 'GOOGLE_CUSTOM_SEARCH_API_KEY');
47
+ this.cx = this.GetSecret('cx', 'GOOGLE_CUSTOM_SEARCH_CX');
48
+ if (this.apiKey && this.cx) {
49
+ this.MarkAvailable();
50
+ }
51
+ else {
52
+ this.MarkUnavailable('Google Custom Search needs both an API key and a search engine ID (cx). ' +
53
+ 'Note the API is closed to new customers and is discontinued on 2027-01-01.');
54
+ }
55
+ }
56
+ async ExecuteSearch(params, _contextUser) {
57
+ const query = {
58
+ key: this.apiKey ?? '',
59
+ cx: this.cx ?? '',
60
+ q: params.Query,
61
+ num: this.ResolveMaxResults(params.MaxResults),
62
+ };
63
+ if (params.SafeSearch) {
64
+ query.safe = params.SafeSearch === 'strict' ? 'active' : 'off';
65
+ }
66
+ // Google takes a single site restriction, so only the first include domain is expressible.
67
+ if (params.IncludeDomains?.length) {
68
+ query.siteSearch = params.IncludeDomains[0];
69
+ query.siteSearchFilter = 'i';
70
+ }
71
+ else if (params.ExcludeDomains?.length) {
72
+ query.siteSearch = params.ExcludeDomains[0];
73
+ query.siteSearchFilter = 'e';
74
+ }
75
+ const dateRestrict = this.mapFreshness(params);
76
+ if (dateRestrict) {
77
+ query.dateRestrict = dateRestrict;
78
+ }
79
+ if (params.Country) {
80
+ query.gl = params.Country.toLowerCase();
81
+ }
82
+ try {
83
+ const response = await HttpGet(GoogleCustomSearchWebSearchProvider_1.ENDPOINT, { Query: query, Timeout: 15000 });
84
+ if (!response.Data) {
85
+ return failure('transient', 'Empty response from Google Custom Search.');
86
+ }
87
+ return {
88
+ Success: true,
89
+ Hits: (response.Data.items ?? []).map((item) => this.toHit(item)),
90
+ };
91
+ }
92
+ catch (e) {
93
+ return classifyHttpFailure(e, 'Google Custom Search');
94
+ }
95
+ }
96
+ /** Google expresses recency as `d7`, `w2`, `m3`, `y1`. */
97
+ mapFreshness(params) {
98
+ const freshness = params.Freshness;
99
+ if (!freshness) {
100
+ return undefined;
101
+ }
102
+ if (typeof freshness === 'string') {
103
+ return { day: 'd1', week: 'w1', month: 'm1', year: 'y1' }[freshness];
104
+ }
105
+ const days = Math.max(1, Math.ceil((freshness.To.getTime() - freshness.From.getTime()) / 86400000));
106
+ return `d${days}`;
107
+ }
108
+ toHit(item) {
109
+ return {
110
+ Title: item.title ?? '',
111
+ URL: item.link ?? '',
112
+ Snippet: item.snippet ?? '',
113
+ DisplayURL: item.displayLink ?? undefined,
114
+ };
115
+ }
116
+ };
117
+ GoogleCustomSearchWebSearchProvider = GoogleCustomSearchWebSearchProvider_1 = __decorate([
118
+ RegisterClass(BaseWebSearchProvider, 'GoogleCustomSearchWebSearchProvider')
119
+ ], GoogleCustomSearchWebSearchProvider);
120
+ export { GoogleCustomSearchWebSearchProvider };
121
+ /** Anti-tree-shaking anchor. */
122
+ export function LoadGoogleCustomSearchWebSearchProvider() {
123
+ void GoogleCustomSearchWebSearchProvider;
124
+ }
125
+ //# sourceMappingURL=GoogleCustomSearchWebSearchProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleCustomSearchWebSearchProvider.js","sourceRoot":"","sources":["../../src/providers/GoogleCustomSearchWebSearchProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @fileoverview Perplexity driver — the raw `/search` endpoint, not the Sonar chat models.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ import { UserInfo } from '@memberjunction/core';
6
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
7
+ import { WebSearchCapabilities, WebSearchParams, WebSearchProviderResponse } from '../types.js';
8
+ /**
9
+ * Searches through Perplexity.
10
+ *
11
+ * Hits always come from **`POST /search`** — the raw search endpoint, which returns ranked
12
+ * title/url/snippet at a flat per-query price in well under a second. The Sonar chat models are
13
+ * used only when {@link WebSearchParams.IncludeAnswer} asks for prose, because they bill per
14
+ * token and take seconds; paying generation prices to obtain a list of links was the defect
15
+ * this driver exists to avoid repeating.
16
+ *
17
+ * Credential key `apiKey`; environment fallback `PERPLEXITY_API_KEY`.
18
+ */
19
+ export declare class PerplexityWebSearchProvider extends BaseWebSearchProvider {
20
+ private static readonly SEARCH_ENDPOINT;
21
+ private static readonly CHAT_ENDPOINT;
22
+ /** Perplexity's cap on the domain allow/deny list. */
23
+ private static readonly MAX_DOMAIN_FILTERS;
24
+ readonly Capabilities: WebSearchCapabilities;
25
+ private apiKey;
26
+ CheckAvailability(_contextUser: UserInfo): Promise<void>;
27
+ ExecuteSearch(params: WebSearchParams, _contextUser: UserInfo): Promise<WebSearchProviderResponse>;
28
+ /**
29
+ * Second call to the Sonar chat models, made only when an answer was explicitly requested.
30
+ *
31
+ * A failure here does not fail the search — the hits are already in hand and are the more
32
+ * valuable half, so the answer is simply absent.
33
+ */
34
+ private fetchAnswer;
35
+ private mapRecency;
36
+ private toHit;
37
+ }
38
+ /** Anti-tree-shaking anchor. */
39
+ export declare function LoadPerplexityWebSearchProvider(): void;
40
+ //# sourceMappingURL=PerplexityWebSearchProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PerplexityWebSearchProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PerplexityWebSearchProvider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAgB,eAAe,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAsB3G;;;;;;;;;;GAUG;AACH,qBACa,2BAA4B,SAAQ,qBAAqB;IAClE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAsC;IAC7E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAgD;IACrF,sDAAsD;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IAEhD,SAAgB,YAAY,EAAE,qBAAqB,CAMjD;IAEF,OAAO,CAAC,MAAM,CAAqB;IAEtB,iBAAiB,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxD,aAAa,CACtB,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,QAAQ,GACvB,OAAO,CAAC,yBAAyB,CAAC;IAuCrC;;;;;OAKG;YACW,WAAW;IAmBzB,OAAO,CAAC,UAAU;IAgBlB,OAAO,CAAC,KAAK;CAiBhB;AAED,gCAAgC;AAChC,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * @fileoverview Perplexity driver — the raw `/search` endpoint, not the Sonar chat models.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var PerplexityWebSearchProvider_1;
12
+ import { RegisterClass } from '@memberjunction/global';
13
+ import { HttpPost } from '@memberjunction/network-utils';
14
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
15
+ import { classifyHttpFailure, failure } from './httpFailure.js';
16
+ /**
17
+ * Searches through Perplexity.
18
+ *
19
+ * Hits always come from **`POST /search`** — the raw search endpoint, which returns ranked
20
+ * title/url/snippet at a flat per-query price in well under a second. The Sonar chat models are
21
+ * used only when {@link WebSearchParams.IncludeAnswer} asks for prose, because they bill per
22
+ * token and take seconds; paying generation prices to obtain a list of links was the defect
23
+ * this driver exists to avoid repeating.
24
+ *
25
+ * Credential key `apiKey`; environment fallback `PERPLEXITY_API_KEY`.
26
+ */
27
+ let PerplexityWebSearchProvider = class PerplexityWebSearchProvider extends BaseWebSearchProvider {
28
+ constructor() {
29
+ super(...arguments);
30
+ this.Capabilities = {
31
+ Answer: true,
32
+ DomainFilter: true,
33
+ Freshness: true,
34
+ Region: true,
35
+ MaxResultsCap: 20,
36
+ };
37
+ }
38
+ static { PerplexityWebSearchProvider_1 = this; }
39
+ static { this.SEARCH_ENDPOINT = 'https://api.perplexity.ai/search'; }
40
+ static { this.CHAT_ENDPOINT = 'https://api.perplexity.ai/chat/completions'; }
41
+ /** Perplexity's cap on the domain allow/deny list. */
42
+ static { this.MAX_DOMAIN_FILTERS = 20; }
43
+ async CheckAvailability(_contextUser) {
44
+ this.apiKey = this.GetSecret('apiKey', 'PERPLEXITY_API_KEY');
45
+ if (this.apiKey) {
46
+ this.MarkAvailable();
47
+ }
48
+ else {
49
+ this.MarkUnavailable('No Perplexity API key. Set it on the linked Credential (key `apiKey`) or as PERPLEXITY_API_KEY.');
50
+ }
51
+ }
52
+ async ExecuteSearch(params, _contextUser) {
53
+ const body = {
54
+ query: params.Query,
55
+ max_results: this.ResolveMaxResults(params.MaxResults),
56
+ };
57
+ // Truncated rather than rejected: Perplexity 400s on an over-long list, and the first
58
+ // 20 domains still express what the caller was narrowing to.
59
+ const domains = (params.IncludeDomains ?? []).slice(0, PerplexityWebSearchProvider_1.MAX_DOMAIN_FILTERS);
60
+ if (domains.length > 0) {
61
+ body.search_domain_filter = domains;
62
+ }
63
+ const recency = this.mapRecency(params);
64
+ if (recency) {
65
+ body.search_recency_filter = recency;
66
+ }
67
+ if (params.Country) {
68
+ body.country = params.Country.toUpperCase();
69
+ }
70
+ try {
71
+ const response = await HttpPost(PerplexityWebSearchProvider_1.SEARCH_ENDPOINT, body, { Headers: { Authorization: `Bearer ${this.apiKey}` }, Timeout: 30000 });
72
+ if (!response.Data) {
73
+ return failure('transient', 'Empty response from Perplexity Search.');
74
+ }
75
+ const hits = (response.Data.results ?? []).map((item) => this.toHit(item));
76
+ const answer = params.IncludeAnswer ? await this.fetchAnswer(params) : undefined;
77
+ return { Success: true, Hits: hits, Answer: answer };
78
+ }
79
+ catch (e) {
80
+ return classifyHttpFailure(e, 'Perplexity');
81
+ }
82
+ }
83
+ /**
84
+ * Second call to the Sonar chat models, made only when an answer was explicitly requested.
85
+ *
86
+ * A failure here does not fail the search — the hits are already in hand and are the more
87
+ * valuable half, so the answer is simply absent.
88
+ */
89
+ async fetchAnswer(params) {
90
+ try {
91
+ const response = await HttpPost(PerplexityWebSearchProvider_1.CHAT_ENDPOINT, {
92
+ model: this.GetConfigValue('answerModel') ?? 'sonar',
93
+ messages: [{ role: 'user', content: params.Query }],
94
+ max_tokens: this.GetConfigValue('answerMaxTokens') ?? 1000,
95
+ stream: false,
96
+ }, { Headers: { Authorization: `Bearer ${this.apiKey}` }, Timeout: 60000 });
97
+ return response.Data?.choices?.[0]?.message?.content || undefined;
98
+ }
99
+ catch {
100
+ // Deliberately swallowed and reported as absent rather than thrown — see the JSDoc.
101
+ return undefined;
102
+ }
103
+ }
104
+ mapRecency(params) {
105
+ const freshness = params.Freshness;
106
+ if (!freshness) {
107
+ return undefined;
108
+ }
109
+ if (typeof freshness === 'string') {
110
+ return freshness;
111
+ }
112
+ // Perplexity takes only relative windows; pick the smallest that covers the range.
113
+ const days = Math.ceil((freshness.To.getTime() - freshness.From.getTime()) / 86400000);
114
+ if (days <= 1)
115
+ return 'day';
116
+ if (days <= 7)
117
+ return 'week';
118
+ if (days <= 31)
119
+ return 'month';
120
+ return 'year';
121
+ }
122
+ toHit(item) {
123
+ const hit = {
124
+ Title: item.title ?? '',
125
+ URL: item.url ?? '',
126
+ Snippet: item.snippet ?? '',
127
+ };
128
+ if (item.date) {
129
+ const parsed = new Date(item.date);
130
+ if (!isNaN(parsed.getTime())) {
131
+ hit.PublishedAt = parsed;
132
+ }
133
+ }
134
+ if (item.last_updated) {
135
+ hit.ProviderExtras = { lastUpdated: item.last_updated };
136
+ }
137
+ return hit;
138
+ }
139
+ };
140
+ PerplexityWebSearchProvider = PerplexityWebSearchProvider_1 = __decorate([
141
+ RegisterClass(BaseWebSearchProvider, 'PerplexityWebSearchProvider')
142
+ ], PerplexityWebSearchProvider);
143
+ export { PerplexityWebSearchProvider };
144
+ /** Anti-tree-shaking anchor. */
145
+ export function LoadPerplexityWebSearchProvider() {
146
+ void PerplexityWebSearchProvider;
147
+ }
148
+ //# sourceMappingURL=PerplexityWebSearchProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PerplexityWebSearchProvider.js","sourceRoot":"","sources":["../../src/providers/PerplexityWebSearchProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @fileoverview Tavily driver — search built for LLM consumption.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ import { UserInfo } from '@memberjunction/core';
6
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
7
+ import { WebSearchCapabilities, WebSearchParams, WebSearchProviderResponse } from '../types.js';
8
+ /**
9
+ * Searches through Tavily, whose results arrive as **extracted page content** rather than
10
+ * snippets plus links to go fetch — which is what makes it well suited to an agent that would
11
+ * otherwise make a second round trip per result.
12
+ *
13
+ * It is an aggregator rather than an index of its own, so it inherits its upstreams' fate;
14
+ * that is why it sits behind Brave in the default priority order rather than ahead of it.
15
+ *
16
+ * Credential key `apiKey`; environment fallback `TAVILY_API_KEY`.
17
+ */
18
+ export declare class TavilyWebSearchProvider extends BaseWebSearchProvider {
19
+ private static readonly ENDPOINT;
20
+ readonly Capabilities: WebSearchCapabilities;
21
+ private apiKey;
22
+ CheckAvailability(_contextUser: UserInfo): Promise<void>;
23
+ ExecuteSearch(params: WebSearchParams, _contextUser: UserInfo): Promise<WebSearchProviderResponse>;
24
+ /** Tavily expresses recency as a day count on its news topic. */
25
+ private freshnessToDays;
26
+ private toHit;
27
+ }
28
+ /** Anti-tree-shaking anchor. */
29
+ export declare function LoadTavilyWebSearchProvider(): void;
30
+ //# sourceMappingURL=TavilyWebSearchProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TavilyWebSearchProvider.d.ts","sourceRoot":"","sources":["../../src/providers/TavilyWebSearchProvider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAgB,eAAe,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAmB3G;;;;;;;;;GASG;AACH,qBACa,uBAAwB,SAAQ,qBAAqB;IAC9D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAEnE,SAAgB,YAAY,EAAE,qBAAqB,CAMjD;IAEF,OAAO,CAAC,MAAM,CAAqB;IAEtB,iBAAiB,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxD,aAAa,CACtB,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,QAAQ,GACvB,OAAO,CAAC,yBAAyB,CAAC;IA2CrC,iEAAiE;IACjE,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,KAAK;CAkBhB;AAED,gCAAgC;AAChC,wBAAgB,2BAA2B,IAAI,IAAI,CAElD"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @fileoverview Tavily driver — search built for LLM consumption.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var TavilyWebSearchProvider_1;
12
+ import { RegisterClass } from '@memberjunction/global';
13
+ import { HttpPost } from '@memberjunction/network-utils';
14
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
15
+ import { classifyHttpFailure, failure } from './httpFailure.js';
16
+ /**
17
+ * Searches through Tavily, whose results arrive as **extracted page content** rather than
18
+ * snippets plus links to go fetch — which is what makes it well suited to an agent that would
19
+ * otherwise make a second round trip per result.
20
+ *
21
+ * It is an aggregator rather than an index of its own, so it inherits its upstreams' fate;
22
+ * that is why it sits behind Brave in the default priority order rather than ahead of it.
23
+ *
24
+ * Credential key `apiKey`; environment fallback `TAVILY_API_KEY`.
25
+ */
26
+ let TavilyWebSearchProvider = class TavilyWebSearchProvider extends BaseWebSearchProvider {
27
+ constructor() {
28
+ super(...arguments);
29
+ this.Capabilities = {
30
+ Answer: true,
31
+ DomainFilter: true,
32
+ Freshness: true,
33
+ Region: false,
34
+ MaxResultsCap: 20,
35
+ };
36
+ }
37
+ static { TavilyWebSearchProvider_1 = this; }
38
+ static { this.ENDPOINT = 'https://api.tavily.com/search'; }
39
+ async CheckAvailability(_contextUser) {
40
+ this.apiKey = this.GetSecret('apiKey', 'TAVILY_API_KEY');
41
+ if (this.apiKey) {
42
+ this.MarkAvailable();
43
+ }
44
+ else {
45
+ this.MarkUnavailable('No Tavily API key. Set it on the linked Credential (key `apiKey`) or as TAVILY_API_KEY.');
46
+ }
47
+ }
48
+ async ExecuteSearch(params, _contextUser) {
49
+ // Tavily dates results only on its news topic, so a freshness request selects that topic.
50
+ const topic = params.Freshness ? 'news' : 'general';
51
+ const body = {
52
+ query: params.Query,
53
+ search_depth: this.GetConfigValue('searchDepth') ?? 'basic',
54
+ topic,
55
+ max_results: this.ResolveMaxResults(params.MaxResults),
56
+ include_answer: params.IncludeAnswer === true,
57
+ include_raw_content: false,
58
+ };
59
+ if (params.IncludeDomains?.length) {
60
+ body.include_domains = params.IncludeDomains;
61
+ }
62
+ if (params.ExcludeDomains?.length) {
63
+ body.exclude_domains = params.ExcludeDomains;
64
+ }
65
+ const days = this.freshnessToDays(params);
66
+ if (days !== undefined) {
67
+ body.days = days;
68
+ }
69
+ try {
70
+ const response = await HttpPost(TavilyWebSearchProvider_1.ENDPOINT, body, {
71
+ Headers: { Authorization: `Bearer ${this.apiKey}` },
72
+ Timeout: 60000,
73
+ });
74
+ if (!response.Data) {
75
+ return failure('transient', 'Empty response from Tavily.');
76
+ }
77
+ return {
78
+ Success: true,
79
+ Hits: (response.Data.results ?? []).map((item) => this.toHit(item)),
80
+ Answer: params.IncludeAnswer ? response.Data.answer : undefined,
81
+ };
82
+ }
83
+ catch (e) {
84
+ return classifyHttpFailure(e, 'Tavily');
85
+ }
86
+ }
87
+ /** Tavily expresses recency as a day count on its news topic. */
88
+ freshnessToDays(params) {
89
+ const freshness = params.Freshness;
90
+ if (!freshness) {
91
+ return undefined;
92
+ }
93
+ if (typeof freshness === 'string') {
94
+ return { day: 1, week: 7, month: 30, year: 365 }[freshness];
95
+ }
96
+ const ms = freshness.To.getTime() - freshness.From.getTime();
97
+ return Math.max(1, Math.ceil(ms / 86400000));
98
+ }
99
+ toHit(item) {
100
+ const hit = {
101
+ Title: item.title ?? '',
102
+ URL: item.url ?? '',
103
+ Snippet: item.content ?? '',
104
+ Score: typeof item.score === 'number' ? item.score : undefined,
105
+ };
106
+ if (item.published_date) {
107
+ const parsed = new Date(item.published_date);
108
+ if (!isNaN(parsed.getTime())) {
109
+ hit.PublishedAt = parsed;
110
+ }
111
+ }
112
+ if (item.raw_content) {
113
+ hit.ProviderExtras = { rawContent: item.raw_content };
114
+ }
115
+ return hit;
116
+ }
117
+ };
118
+ TavilyWebSearchProvider = TavilyWebSearchProvider_1 = __decorate([
119
+ RegisterClass(BaseWebSearchProvider, 'TavilyWebSearchProvider')
120
+ ], TavilyWebSearchProvider);
121
+ export { TavilyWebSearchProvider };
122
+ /** Anti-tree-shaking anchor. */
123
+ export function LoadTavilyWebSearchProvider() {
124
+ void TavilyWebSearchProvider;
125
+ }
126
+ //# sourceMappingURL=TavilyWebSearchProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TavilyWebSearchProvider.js","sourceRoot":"","sources":["../../src/providers/TavilyWebSearchProvider.ts"],"names":[],"mappings":"AAAA;;;GAGG;;;;;;;;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAkB7D;;;;;;;;;GASG;AAEI,IAAM,uBAAuB,GAA7B,MAAM,uBAAwB,SAAQ,qBAAqB;IAA3D;;QAGa,iBAAY,GAA0B;YAClD,MAAM,EAAE,IAAI;YACZ,YAAY,EAAE,IAAI;YAClB,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,KAAK;YACb,aAAa,EAAE,EAAE;SACpB,CAAC;IA4FN,CAAC;;aApG2B,aAAQ,GAAG,+BAA+B,AAAlC,CAAmC;IAY5D,KAAK,CAAC,iBAAiB,CAAC,YAAsB;QACjD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;QACzD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,aAAa,EAAE,CAAC;QACzB,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,eAAe,CAChB,yFAAyF,CAC5F,CAAC;QACN,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,aAAa,CACtB,MAAuB,EACvB,YAAsB;QAEtB,0FAA0F;QAC1F,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QACpD,MAAM,IAAI,GAA4B;YAClC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,YAAY,EAAE,IAAI,CAAC,cAAc,CAAS,aAAa,CAAC,IAAI,OAAO;YACnE,KAAK;YACL,WAAW,EAAE,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC;YACtD,cAAc,EAAE,MAAM,CAAC,aAAa,KAAK,IAAI;YAC7C,mBAAmB,EAAE,KAAK;SAC7B,CAAC;QAEF,IAAI,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;YAChC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC;QACjD,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;YAChC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,cAAc,CAAC;QACjD,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,CAAC;QAED,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAoB,yBAAuB,CAAC,QAAQ,EAAE,IAAI,EAAE;gBACvF,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE,EAAE;gBACnD,OAAO,EAAE,KAAK;aACjB,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,OAAO,CAAC,WAAW,EAAE,6BAA6B,CAAC,CAAC;YAC/D,CAAC;YAED,OAAO;gBACH,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACnE,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;aAClE,CAAC;QACN,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO,mBAAmB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC5C,CAAC;IACL,CAAC;IAED,iEAAiE;IACzD,eAAe,CAAC,MAAuB;QAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC;IACjD,CAAC;IAEO,KAAK,CAAC,IAAqB;QAC/B,MAAM,GAAG,GAAiB;YACtB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;YACvB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE;YAC3B,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;SACjE,CAAC;QACF,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;gBAC3B,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC;YAC7B,CAAC;QACL,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,GAAG,CAAC,cAAc,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1D,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;;AApGQ,uBAAuB;IADnC,aAAa,CAAC,qBAAqB,EAAE,yBAAyB,CAAC;GACnD,uBAAuB,CAqGnC;;AAED,gCAAgC;AAChC,MAAM,UAAU,2BAA2B;IACvC,KAAK,uBAAuB,CAAC;AACjC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @fileoverview Shared HTTP failure classification for web search drivers.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ import { WebSearchFailureKind, WebSearchProviderResponse } from '../types.js';
6
+ /**
7
+ * Classify an error thrown by a vendor call into the kind that drives engine failover.
8
+ *
9
+ * The distinction is about **the request, not the vendor**: a 400/422 normally means this query is
10
+ * malformed and every other provider will reject it too, so the engine must stop rather than
11
+ * spend four more paid calls learning the same thing. Everything else — auth, quota, 5xx,
12
+ * timeouts — is specific to this vendor and another may well succeed.
13
+ *
14
+ * A missing or invalid API key is deliberately `transient` in that sense: the request is fine,
15
+ * *this* provider just cannot serve it, so moving on is exactly right.
16
+ *
17
+ * **Status alone does not decide that**, which is why a 400 is inspected before it is trusted.
18
+ * Google Custom Search answers an invalid key with HTTP 400 (`reason: keyInvalid`), not 401 — so
19
+ * classifying by status alone made a dead Google key stop the whole run, on precisely the vendor
20
+ * whose keys are expected to stop working when the API is discontinued on 2027-01-01. Perplexity
21
+ * answers the same condition with 401 and failed over correctly, which is what made the
22
+ * inconsistency easy to miss.
23
+ */
24
+ export declare function classifyHttpFailure(error: unknown, vendor: string): WebSearchProviderResponse;
25
+ /** Build a failed provider response. */
26
+ export declare function failure(kind: WebSearchFailureKind, message: string): WebSearchProviderResponse;
27
+ /** Map a relative freshness window onto a vendor's own code table. */
28
+ export declare function mapFreshnessWindow<T extends string>(window: 'day' | 'week' | 'month' | 'year', codes: Record<'day' | 'week' | 'month' | 'year', T>): T;
29
+ //# sourceMappingURL=httpFailure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpFailure.d.ts","sourceRoot":"","sources":["../../src/providers/httpFailure.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAyD3E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,yBAAyB,CA4B7F;AAED,wCAAwC;AACxC,wBAAgB,OAAO,CAAC,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,GAAG,yBAAyB,CAE9F;AA8BD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAC/C,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EACzC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC,GACpD,CAAC,CAEH"}