@memberjunction/search-engine 5.33.0 → 5.34.1

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 (87) hide show
  1. package/dist/generic/BaseReRanker.d.ts +164 -0
  2. package/dist/generic/BaseReRanker.d.ts.map +1 -0
  3. package/dist/generic/BaseReRanker.js +209 -0
  4. package/dist/generic/BaseReRanker.js.map +1 -0
  5. package/dist/generic/EntitySearchProvider.d.ts +36 -4
  6. package/dist/generic/EntitySearchProvider.d.ts.map +1 -1
  7. package/dist/generic/EntitySearchProvider.js +95 -19
  8. package/dist/generic/EntitySearchProvider.js.map +1 -1
  9. package/dist/generic/FullTextSearchProvider.d.ts +2 -2
  10. package/dist/generic/FullTextSearchProvider.d.ts.map +1 -1
  11. package/dist/generic/FullTextSearchProvider.js +14 -3
  12. package/dist/generic/FullTextSearchProvider.js.map +1 -1
  13. package/dist/generic/ISearchProvider.d.ts +44 -2
  14. package/dist/generic/ISearchProvider.d.ts.map +1 -1
  15. package/dist/generic/ISearchProvider.js +35 -1
  16. package/dist/generic/ISearchProvider.js.map +1 -1
  17. package/dist/generic/NoopReRanker.d.ts +28 -0
  18. package/dist/generic/NoopReRanker.d.ts.map +1 -0
  19. package/dist/generic/NoopReRanker.js +49 -0
  20. package/dist/generic/NoopReRanker.js.map +1 -0
  21. package/dist/generic/ScopeTemplateRenderer.d.ts +36 -0
  22. package/dist/generic/ScopeTemplateRenderer.d.ts.map +1 -0
  23. package/dist/generic/ScopeTemplateRenderer.js +110 -0
  24. package/dist/generic/ScopeTemplateRenderer.js.map +1 -0
  25. package/dist/generic/SearchEngine.d.ts +154 -12
  26. package/dist/generic/SearchEngine.d.ts.map +1 -1
  27. package/dist/generic/SearchEngine.js +662 -39
  28. package/dist/generic/SearchEngine.js.map +1 -1
  29. package/dist/generic/SearchFusion.d.ts +40 -6
  30. package/dist/generic/SearchFusion.d.ts.map +1 -1
  31. package/dist/generic/SearchFusion.js +139 -18
  32. package/dist/generic/SearchFusion.js.map +1 -1
  33. package/dist/generic/StorageSearchProvider.d.ts +9 -2
  34. package/dist/generic/StorageSearchProvider.d.ts.map +1 -1
  35. package/dist/generic/StorageSearchProvider.js +44 -12
  36. package/dist/generic/StorageSearchProvider.js.map +1 -1
  37. package/dist/generic/VectorSearchProvider.d.ts +9 -2
  38. package/dist/generic/VectorSearchProvider.d.ts.map +1 -1
  39. package/dist/generic/VectorSearchProvider.js +83 -13
  40. package/dist/generic/VectorSearchProvider.js.map +1 -1
  41. package/dist/generic/search.types.d.ts +206 -0
  42. package/dist/generic/search.types.d.ts.map +1 -1
  43. package/dist/index.d.ts +13 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +18 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/permissions/SearchScopePermissionResolver.d.ts +109 -0
  48. package/dist/permissions/SearchScopePermissionResolver.d.ts.map +1 -0
  49. package/dist/permissions/SearchScopePermissionResolver.js +159 -0
  50. package/dist/permissions/SearchScopePermissionResolver.js.map +1 -0
  51. package/dist/providers/AzureAISearchProvider.d.ts +37 -0
  52. package/dist/providers/AzureAISearchProvider.d.ts.map +1 -0
  53. package/dist/providers/AzureAISearchProvider.js +180 -0
  54. package/dist/providers/AzureAISearchProvider.js.map +1 -0
  55. package/dist/providers/ElasticsearchSearchProvider.d.ts +43 -0
  56. package/dist/providers/ElasticsearchSearchProvider.d.ts.map +1 -0
  57. package/dist/providers/ElasticsearchSearchProvider.js +200 -0
  58. package/dist/providers/ElasticsearchSearchProvider.js.map +1 -0
  59. package/dist/providers/OpenSearchSearchProvider.d.ts +36 -0
  60. package/dist/providers/OpenSearchSearchProvider.d.ts.map +1 -0
  61. package/dist/providers/OpenSearchSearchProvider.js +167 -0
  62. package/dist/providers/OpenSearchSearchProvider.js.map +1 -0
  63. package/dist/providers/TypesenseSearchProvider.d.ts +36 -0
  64. package/dist/providers/TypesenseSearchProvider.d.ts.map +1 -0
  65. package/dist/providers/TypesenseSearchProvider.js +161 -0
  66. package/dist/providers/TypesenseSearchProvider.js.map +1 -0
  67. package/dist/rerankers/BGEReRanker.d.ts +57 -0
  68. package/dist/rerankers/BGEReRanker.d.ts.map +1 -0
  69. package/dist/rerankers/BGEReRanker.js +193 -0
  70. package/dist/rerankers/BGEReRanker.js.map +1 -0
  71. package/dist/rerankers/CohereReRanker.d.ts +65 -0
  72. package/dist/rerankers/CohereReRanker.d.ts.map +1 -0
  73. package/dist/rerankers/CohereReRanker.js +155 -0
  74. package/dist/rerankers/CohereReRanker.js.map +1 -0
  75. package/dist/rerankers/OpenAIReRanker.d.ts +62 -0
  76. package/dist/rerankers/OpenAIReRanker.d.ts.map +1 -0
  77. package/dist/rerankers/OpenAIReRanker.js +197 -0
  78. package/dist/rerankers/OpenAIReRanker.js.map +1 -0
  79. package/dist/rerankers/RerankerBudgetGuard.d.ts +54 -0
  80. package/dist/rerankers/RerankerBudgetGuard.d.ts.map +1 -0
  81. package/dist/rerankers/RerankerBudgetGuard.js +67 -0
  82. package/dist/rerankers/RerankerBudgetGuard.js.map +1 -0
  83. package/dist/rerankers/VoyageReRanker.d.ts +59 -0
  84. package/dist/rerankers/VoyageReRanker.d.ts.map +1 -0
  85. package/dist/rerankers/VoyageReRanker.js +184 -0
  86. package/dist/rerankers/VoyageReRanker.js.map +1 -0
  87. package/package.json +13 -8
@@ -0,0 +1,197 @@
1
+ /**
2
+ * @fileoverview OpenAI chat-judge re-ranker (P2D.4).
3
+ *
4
+ * **Decision (2026-04-28):** OpenAI does not publish a first-party reranker endpoint.
5
+ * Their offerings are completions, chat, embeddings, and moderation — there is no
6
+ * dedicated `/rerank`. This implementation uses the documented fallback from the plan:
7
+ * a small chat-completion-based "judge" that scores each candidate's relevance to the
8
+ * query and returns the new ordering. Re-evaluate this decision when OpenAI ships a
9
+ * first-party reranker — the canonical plan calls out swapping in the first-party
10
+ * endpoint as soon as it exists.
11
+ *
12
+ * **Approach.** Single chat completion call, JSON-mode response. The model receives
13
+ * the query and a numbered list of documents and returns
14
+ * `{ "scores": [<number 0..1>, ...] }` with one score per document, in input order.
15
+ * We then sort by score, slice to topN, and return.
16
+ *
17
+ * **Cost.** `gpt-4o-mini` defaults: $0.15 / 1M input tokens, $0.60 / 1M output tokens
18
+ * (2026-04 pricing). Pre-call estimate uses a conservative ~80 tokens/doc input plus
19
+ * 8 tokens/doc output. Post-call cost reports the exact `usage.{prompt,completion}_tokens`
20
+ * from OpenAI so the budget guard sees real spend.
21
+ *
22
+ * **API key.** `GetAIAPIKey('OpenAILLM')` —
23
+ * AI_VENDOR_API_KEY__OPENAILLM or any registered AIAPIKeys subclass.
24
+ *
25
+ * @module @memberjunction/search-engine
26
+ */
27
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
28
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
29
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
30
+ 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;
31
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
32
+ };
33
+ var OpenAIReRanker_1;
34
+ import { RegisterClass } from '@memberjunction/global';
35
+ import { LogError } from '@memberjunction/core';
36
+ import { BaseReranker as AIBaseReranker, GetAIAPIKey, } from '@memberjunction/ai';
37
+ import { BaseReRanker } from '../generic/BaseReRanker.js';
38
+ const OPENAI_CHAT_URL = 'https://api.openai.com/v1/chat/completions';
39
+ const SYSTEM_PROMPT = [
40
+ 'You are a precise relevance scorer for a search system.',
41
+ 'Given a user query and a numbered list of candidate documents, return a JSON object',
42
+ 'with a single key "scores" whose value is an array of numbers. The array must have',
43
+ 'exactly one entry per input document, in the same order, where each entry is a',
44
+ 'relevance score in [0, 1] representing how well that document answers the query.',
45
+ 'Use the full range — do not cluster all scores in one band. Output JSON only.',
46
+ ].join('\n');
47
+ class OpenAIScopeAIReranker extends AIBaseReranker {
48
+ constructor(apiKey, modelName) {
49
+ super(apiKey, modelName);
50
+ /** Last call's prompt+completion tokens, populated by `doRerank`. */
51
+ this.lastPromptTokens = 0;
52
+ this.lastCompletionTokens = 0;
53
+ }
54
+ async doRerank(params) {
55
+ const docList = params.documents
56
+ .map((d, i) => `[${i}] ${d.text}`)
57
+ .join('\n');
58
+ const userPrompt = `Query:\n${params.query}\n\nDocuments:\n${docList}`;
59
+ const response = await fetch(OPENAI_CHAT_URL, {
60
+ method: 'POST',
61
+ headers: {
62
+ 'Authorization': `Bearer ${this.apiKey}`,
63
+ 'Content-Type': 'application/json',
64
+ },
65
+ body: JSON.stringify({
66
+ model: this._modelName,
67
+ messages: [
68
+ { role: 'system', content: SYSTEM_PROMPT },
69
+ { role: 'user', content: userPrompt },
70
+ ],
71
+ response_format: { type: 'json_object' },
72
+ temperature: 0,
73
+ }),
74
+ });
75
+ if (!response.ok) {
76
+ const text = await response.text().catch(() => '');
77
+ throw new Error(`OpenAI rerank-judge call failed (${response.status}): ${text || response.statusText}`);
78
+ }
79
+ const json = await response.json();
80
+ this.lastPromptTokens = json.usage?.prompt_tokens ?? 0;
81
+ this.lastCompletionTokens = json.usage?.completion_tokens ?? 0;
82
+ const content = json.choices[0]?.message.content ?? '';
83
+ const parsed = JSON.parse(content);
84
+ if (!Array.isArray(parsed.scores)) {
85
+ throw new Error(`OpenAI rerank-judge returned malformed scores: ${content}`);
86
+ }
87
+ const scores = parsed.scores;
88
+ if (scores.length !== params.documents.length) {
89
+ throw new Error(`OpenAI rerank-judge returned ${scores.length} scores for ${params.documents.length} documents`);
90
+ }
91
+ // Build (doc, score) pairs, sort descending by score, optionally slice topK.
92
+ const pairs = params.documents.map((d, i) => ({
93
+ doc: d,
94
+ score: typeof scores[i] === 'number' ? scores[i] : 0,
95
+ }));
96
+ pairs.sort((a, b) => b.score - a.score);
97
+ const topK = params.topK ?? pairs.length;
98
+ return pairs.slice(0, topK).map((p, idx) => ({
99
+ id: p.doc.id,
100
+ relevanceScore: p.score,
101
+ document: p.doc,
102
+ rank: idx,
103
+ }));
104
+ }
105
+ }
106
+ /**
107
+ * Chat-completion-based "judge" reranker. Configure per scope via
108
+ * `SearchScope.ScopeConfig.reRanker.driverClass = 'OpenAIReRanker'`.
109
+ *
110
+ * Override the model via `ScopeConfig.reRanker.config.model`
111
+ * (default: `gpt-4o-mini` — change when a first-party OpenAI rerank endpoint launches).
112
+ */
113
+ let OpenAIReRanker = class OpenAIReRanker extends BaseReRanker {
114
+ constructor() {
115
+ super(...arguments);
116
+ this.currentAIReranker = null;
117
+ }
118
+ static { OpenAIReRanker_1 = this; }
119
+ /** Pricing in cents-per-token. As of 2026-04. */
120
+ static { this.PRICING = {
121
+ 'gpt-4o-mini': { input: 0.15 / 1_000_000 * 100, output: 0.60 / 1_000_000 * 100 },
122
+ 'gpt-4o': { input: 2.50 / 1_000_000 * 100, output: 10.00 / 1_000_000 * 100 },
123
+ 'gpt-4.1-mini': { input: 0.40 / 1_000_000 * 100, output: 1.60 / 1_000_000 * 100 },
124
+ 'gpt-4.1': { input: 2.00 / 1_000_000 * 100, output: 8.00 / 1_000_000 * 100 },
125
+ }; }
126
+ get DriverClass() {
127
+ return 'OpenAIReRanker';
128
+ }
129
+ get Name() {
130
+ return 'OpenAI';
131
+ }
132
+ get Version() {
133
+ return '1';
134
+ }
135
+ /**
136
+ * Effective limit when the candidate list plus prompt fits in the model's input
137
+ * window. With gpt-4o-mini's 128k input context and ~80 tokens/doc, the practical
138
+ * cap is well above 1000. Returning 1000 to match the other rerankers; the
139
+ * SearchEngine will chunk above this when chunking is implemented.
140
+ */
141
+ GetMaxResultCount() {
142
+ return 1000;
143
+ }
144
+ /**
145
+ * Pre-call cost estimate. ~80 input tokens/doc + 50 system/query tokens, plus
146
+ * 8 output tokens/doc. Conservative — actual usage tends to be lower.
147
+ */
148
+ EstimateCostCents(resultCount) {
149
+ const pricing = OpenAIReRanker_1.PRICING['gpt-4o-mini'];
150
+ const inputTokens = resultCount * 80 + 50;
151
+ const outputTokens = resultCount * 8;
152
+ return inputTokens * pricing.input + outputTokens * pricing.output;
153
+ }
154
+ getAIReranker(config, _contextUser) {
155
+ if (this.currentAIReranker)
156
+ return this.currentAIReranker;
157
+ const apiKey = GetAIAPIKey('OpenAILLM');
158
+ if (!apiKey) {
159
+ LogError('OpenAIReRanker: no API key found for driver "OpenAILLM" — set AI_VENDOR_API_KEY__OPENAILLM or register an AIAPIKeys subclass.');
160
+ return null;
161
+ }
162
+ const modelName = config?.['model'] ?? 'gpt-4o-mini';
163
+ this.currentAIReranker = new OpenAIScopeAIReranker(apiKey, modelName);
164
+ return this.currentAIReranker;
165
+ }
166
+ async ReRank(query, candidates, topN, contextUser, config) {
167
+ if (topN <= 0 || candidates.length === 0)
168
+ return [];
169
+ this.currentAIReranker = null;
170
+ const apiReranker = this.getAIReranker(config, contextUser);
171
+ try {
172
+ if (!apiReranker)
173
+ return candidates.slice(0, topN);
174
+ const result = await super.ReRank(query, candidates, topN, contextUser, config);
175
+ const modelName = config?.['model'] ?? 'gpt-4o-mini';
176
+ const pricing = OpenAIReRanker_1.PRICING[modelName] ?? OpenAIReRanker_1.PRICING['gpt-4o-mini'];
177
+ const cents = apiReranker.lastPromptTokens * pricing.input
178
+ + apiReranker.lastCompletionTokens * pricing.output;
179
+ if (cents > 0)
180
+ this.reportCost(cents);
181
+ return result;
182
+ }
183
+ finally {
184
+ this.currentAIReranker = null;
185
+ }
186
+ }
187
+ };
188
+ OpenAIReRanker = OpenAIReRanker_1 = __decorate([
189
+ RegisterClass(BaseReRanker, 'OpenAIReRanker')
190
+ ], OpenAIReRanker);
191
+ export { OpenAIReRanker };
192
+ /** Tree-shake prevention helper. */
193
+ export function LoadOpenAIReRanker() {
194
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
195
+ const _ref = OpenAIReRanker;
196
+ }
197
+ //# sourceMappingURL=OpenAIReRanker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenAIReRanker.js","sourceRoot":"","sources":["../../src/rerankers/OpenAIReRanker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @fileoverview Reranker budget guard (P2D.6).
3
+ *
4
+ * Per-search-invocation circuit breaker that caps real-provider reranker spend
5
+ * (Cohere, Voyage, OpenAI) against `SearchScope.RerankerBudgetCents`. Wired into
6
+ * the SearchEngine's rerank stage so:
7
+ *
8
+ * 1. Before each rerank call, the engine asks the guard whether `EstimateCostCents`
9
+ * fits in the remaining budget. If not, the engine SKIPS the rerank and uses
10
+ * the unranked candidate list.
11
+ * 2. After each rerank call, the reranker's `CostReporter` callback (P2D.1
12
+ * contract) lands in `record()`, accumulating actual spend.
13
+ *
14
+ * NULL budget = uncapped; the guard always returns `canSpend = true` and accumulates
15
+ * spend purely for observability. This preserves Phase 1 behavior for any scope the
16
+ * admin hasn't explicitly capped.
17
+ *
18
+ * @module @memberjunction/search-engine
19
+ */
20
+ /**
21
+ * Per-run budget tracker. One instance per search invocation; lives only as long
22
+ * as the SearchEngine.search call. Not thread-safe, but Node is single-threaded
23
+ * within a request so that's fine.
24
+ */
25
+ export declare class RerankerBudgetGuard {
26
+ /** Total budget in cents, or null when uncapped. */
27
+ readonly Budget: number | null;
28
+ /** Accumulated cost in cents, populated via record(). */
29
+ Spent: number;
30
+ constructor(budgetCents: number | null | undefined);
31
+ /**
32
+ * Cents remaining in the budget. Returns null when uncapped, else
33
+ * `max(0, Budget - Spent)`.
34
+ */
35
+ Remaining(): number | null;
36
+ /**
37
+ * Pre-call check: does `estimatedCents` fit in the remaining budget? Returns
38
+ * true when uncapped or when remaining ≥ estimate.
39
+ *
40
+ * Negative estimates are treated as zero (defensive — a buggy
41
+ * `EstimateCostCents` shouldn't trigger short-circuits prematurely).
42
+ */
43
+ CanSpend(estimatedCents: number): boolean;
44
+ /**
45
+ * Post-call accumulator. Negative or non-finite values are clamped to 0.
46
+ */
47
+ Record(actualCents: number): void;
48
+ /**
49
+ * Sugar for wiring into a reranker. Returns a closure that records cost on
50
+ * this guard — assignable directly to `BaseReRanker.CostReporter`.
51
+ */
52
+ AsCostReporter(): (cents: number) => void;
53
+ }
54
+ //# sourceMappingURL=RerankerBudgetGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RerankerBudgetGuard.d.ts","sourceRoot":"","sources":["../../src/rerankers/RerankerBudgetGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;GAIG;AACH,qBAAa,mBAAmB;IAC5B,oDAAoD;IACpD,SAAgB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,yDAAyD;IAClD,KAAK,SAAK;gBAEL,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAIlD;;;OAGG;IACI,SAAS,IAAI,MAAM,GAAG,IAAI;IAIjC;;;;;;OAMG;IACI,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO;IAMhD;;OAEG;IACI,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKxC;;;OAGG;IACI,cAAc,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;CAGnD"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @fileoverview Reranker budget guard (P2D.6).
3
+ *
4
+ * Per-search-invocation circuit breaker that caps real-provider reranker spend
5
+ * (Cohere, Voyage, OpenAI) against `SearchScope.RerankerBudgetCents`. Wired into
6
+ * the SearchEngine's rerank stage so:
7
+ *
8
+ * 1. Before each rerank call, the engine asks the guard whether `EstimateCostCents`
9
+ * fits in the remaining budget. If not, the engine SKIPS the rerank and uses
10
+ * the unranked candidate list.
11
+ * 2. After each rerank call, the reranker's `CostReporter` callback (P2D.1
12
+ * contract) lands in `record()`, accumulating actual spend.
13
+ *
14
+ * NULL budget = uncapped; the guard always returns `canSpend = true` and accumulates
15
+ * spend purely for observability. This preserves Phase 1 behavior for any scope the
16
+ * admin hasn't explicitly capped.
17
+ *
18
+ * @module @memberjunction/search-engine
19
+ */
20
+ /**
21
+ * Per-run budget tracker. One instance per search invocation; lives only as long
22
+ * as the SearchEngine.search call. Not thread-safe, but Node is single-threaded
23
+ * within a request so that's fine.
24
+ */
25
+ export class RerankerBudgetGuard {
26
+ constructor(budgetCents) {
27
+ /** Accumulated cost in cents, populated via record(). */
28
+ this.Spent = 0;
29
+ this.Budget = budgetCents == null ? null : Math.max(0, budgetCents);
30
+ }
31
+ /**
32
+ * Cents remaining in the budget. Returns null when uncapped, else
33
+ * `max(0, Budget - Spent)`.
34
+ */
35
+ Remaining() {
36
+ return this.Budget == null ? null : Math.max(0, this.Budget - this.Spent);
37
+ }
38
+ /**
39
+ * Pre-call check: does `estimatedCents` fit in the remaining budget? Returns
40
+ * true when uncapped or when remaining ≥ estimate.
41
+ *
42
+ * Negative estimates are treated as zero (defensive — a buggy
43
+ * `EstimateCostCents` shouldn't trigger short-circuits prematurely).
44
+ */
45
+ CanSpend(estimatedCents) {
46
+ if (this.Budget == null)
47
+ return true;
48
+ const need = Math.max(0, estimatedCents);
49
+ return this.Remaining() >= need;
50
+ }
51
+ /**
52
+ * Post-call accumulator. Negative or non-finite values are clamped to 0.
53
+ */
54
+ Record(actualCents) {
55
+ if (!Number.isFinite(actualCents) || actualCents <= 0)
56
+ return;
57
+ this.Spent += actualCents;
58
+ }
59
+ /**
60
+ * Sugar for wiring into a reranker. Returns a closure that records cost on
61
+ * this guard — assignable directly to `BaseReRanker.CostReporter`.
62
+ */
63
+ AsCostReporter() {
64
+ return (cents) => this.Record(cents);
65
+ }
66
+ }
67
+ //# sourceMappingURL=RerankerBudgetGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RerankerBudgetGuard.js","sourceRoot":"","sources":["../../src/rerankers/RerankerBudgetGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;GAIG;AACH,MAAM,OAAO,mBAAmB;IAM5B,YAAY,WAAsC;QAHlD,yDAAyD;QAClD,UAAK,GAAG,CAAC,CAAC;QAGb,IAAI,CAAC,MAAM,GAAG,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IACxE,CAAC;IAED;;;OAGG;IACI,SAAS;QACZ,OAAO,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;OAMG;IACI,QAAQ,CAAC,cAAsB;QAClC,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC,SAAS,EAAG,IAAI,IAAI,CAAC;IACrC,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,WAAmB;QAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC;YAAE,OAAO;QAC9D,IAAI,CAAC,KAAK,IAAI,WAAW,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACI,cAAc;QACjB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;CACJ"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @fileoverview Voyage AI Rerank API integration (P2D.3).
3
+ *
4
+ * Calls Voyage's `rerank-2` (and `rerank-2-lite`) endpoint via the public REST API.
5
+ * No SDK dependency — Voyage's surface is small and fetch-based, so we call the
6
+ * endpoint directly and avoid pulling in `voyageai` as another optional peer.
7
+ *
8
+ * **Cost model.** Voyage bills per token (query + document tokens combined). Pricing
9
+ * as of 2026-04: `rerank-2` is $0.05 per 1M tokens; `rerank-2-lite` is $0.02 per 1M
10
+ * tokens. We approximate token count as `ceil(charCount / 4)` for the estimate, then
11
+ * report the exact `usage.total_tokens` returned by the API as the actual cost.
12
+ *
13
+ * **API key resolution.** Uses `GetAIAPIKey('VoyageLLM')` — sources from
14
+ * `AI_VENDOR_API_KEY__VOYAGELLM` env var or any registered AIAPIKeys subclass.
15
+ *
16
+ * @module @memberjunction/search-engine
17
+ */
18
+ import { UserInfo } from '@memberjunction/core';
19
+ import { BaseReranker as AIBaseReranker } from '@memberjunction/ai';
20
+ import { BaseReRanker } from '../generic/BaseReRanker.js';
21
+ import { SearchResultItem } from '../generic/search.types.js';
22
+ /**
23
+ * Voyage `rerank-2` re-ranker. Configure per scope via
24
+ * `SearchScope.ScopeConfig.reRanker.driverClass = 'VoyageReRanker'`.
25
+ *
26
+ * Override the model via `ScopeConfig.reRanker.config.model = 'rerank-2-lite'` for
27
+ * the cheaper lite variant.
28
+ */
29
+ export declare class VoyageReRanker extends BaseReRanker {
30
+ /** Pricing in cents-per-token, indexed by model name. As of 2026-04. */
31
+ private static readonly CENTS_PER_TOKEN;
32
+ /**
33
+ * Per-instance reranker, captured so we can read `lastTokensUsed` after a call to
34
+ * report exact cost. Recreated per ReRank invocation when config or contextUser
35
+ * change shouldn't be a concern in practice.
36
+ */
37
+ private currentAIReranker;
38
+ get DriverClass(): string;
39
+ get Name(): string;
40
+ get Version(): string;
41
+ /** Voyage's published cap is 1000 documents per call. */
42
+ GetMaxResultCount(): number;
43
+ /**
44
+ * Pre-call cost estimate using `ceil(charCount / 4)` per document plus query
45
+ * tokens. Conservative — tends to over-estimate, which is what a budget guard
46
+ * wants. Default model is `rerank-2`; pass `config.model` to switch.
47
+ */
48
+ EstimateCostCents(resultCount: number): number;
49
+ protected getAIReranker(config: Record<string, unknown> | undefined, _contextUser: UserInfo): AIBaseReranker | null;
50
+ /**
51
+ * Override `ReRank` to report exact post-call cost via `usage.total_tokens` from
52
+ * the Voyage response, not the pre-call estimate. The estimate exists only to feed
53
+ * the budget guard's pre-call short-circuit.
54
+ */
55
+ ReRank(query: string, candidates: SearchResultItem[], topN: number, contextUser: UserInfo, config?: Record<string, unknown>): Promise<SearchResultItem[]>;
56
+ }
57
+ /** Tree-shake prevention helper. */
58
+ export declare function LoadVoyageReRanker(): void;
59
+ //# sourceMappingURL=VoyageReRanker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VoyageReRanker.d.ts","sourceRoot":"","sources":["../../src/rerankers/VoyageReRanker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EACH,YAAY,IAAI,cAAc,EAIjC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAyE3D;;;;;;GAMG;AACH,qBACa,cAAe,SAAQ,YAAY;IAC5C,wEAAwE;IACxE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAGrC;IAEF;;;;OAIG;IACH,OAAO,CAAC,iBAAiB,CAAsC;IAE/D,IAAW,WAAW,IAAI,MAAM,CAE/B;IACD,IAAoB,IAAI,IAAI,MAAM,CAEjC;IACD,IAAoB,OAAO,IAAI,MAAM,CAEpC;IAED,yDAAyD;IACzC,iBAAiB,IAAI,MAAM;IAI3C;;;;OAIG;IACa,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;cAS3C,aAAa,CAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC3C,YAAY,EAAE,QAAQ,GACvB,cAAc,GAAG,IAAI;IAiBxB;;;;OAIG;IACmB,MAAM,CACxB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,gBAAgB,EAAE,EAC9B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,QAAQ,EACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAsBjC;AAED,oCAAoC;AACpC,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * @fileoverview Voyage AI Rerank API integration (P2D.3).
3
+ *
4
+ * Calls Voyage's `rerank-2` (and `rerank-2-lite`) endpoint via the public REST API.
5
+ * No SDK dependency — Voyage's surface is small and fetch-based, so we call the
6
+ * endpoint directly and avoid pulling in `voyageai` as another optional peer.
7
+ *
8
+ * **Cost model.** Voyage bills per token (query + document tokens combined). Pricing
9
+ * as of 2026-04: `rerank-2` is $0.05 per 1M tokens; `rerank-2-lite` is $0.02 per 1M
10
+ * tokens. We approximate token count as `ceil(charCount / 4)` for the estimate, then
11
+ * report the exact `usage.total_tokens` returned by the API as the actual cost.
12
+ *
13
+ * **API key resolution.** Uses `GetAIAPIKey('VoyageLLM')` — sources from
14
+ * `AI_VENDOR_API_KEY__VOYAGELLM` env var or any registered AIAPIKeys subclass.
15
+ *
16
+ * @module @memberjunction/search-engine
17
+ */
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ 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;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var VoyageReRanker_1;
25
+ import { RegisterClass } from '@memberjunction/global';
26
+ import { LogError } from '@memberjunction/core';
27
+ import { BaseReranker as AIBaseReranker, GetAIAPIKey, } from '@memberjunction/ai';
28
+ import { BaseReRanker } from '../generic/BaseReRanker.js';
29
+ const VOYAGE_RERANK_URL = 'https://api.voyageai.com/v1/rerank';
30
+ /**
31
+ * Approximate Voyage's tokenizer by `ceil(charCount / 4)`. Real Voyage tokenization is
32
+ * BPE-based; this approximation is conservative enough to keep budget guards from
33
+ * under-counting in practice.
34
+ */
35
+ function estimateTokens(text) {
36
+ return Math.max(1, Math.ceil(text.length / 4));
37
+ }
38
+ /**
39
+ * Inline AI-layer reranker that POSTs to Voyage's rerank endpoint.
40
+ */
41
+ class VoyageScopeAIReranker extends AIBaseReranker {
42
+ constructor(apiKey, modelName) {
43
+ super(apiKey, modelName);
44
+ /**
45
+ * Last-call usage. Populated on each `doRerank()` so the SearchEngine wrapper can
46
+ * report exact cost via `usage.total_tokens` instead of the pre-call estimate.
47
+ */
48
+ this.lastTokensUsed = 0;
49
+ }
50
+ async doRerank(params) {
51
+ const body = {
52
+ query: params.query,
53
+ documents: params.documents.map(d => d.text),
54
+ model: this._modelName,
55
+ top_k: params.topK ?? params.documents.length,
56
+ return_documents: false,
57
+ };
58
+ const response = await fetch(VOYAGE_RERANK_URL, {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Authorization': `Bearer ${this.apiKey}`,
62
+ 'Content-Type': 'application/json',
63
+ },
64
+ body: JSON.stringify(body),
65
+ });
66
+ if (!response.ok) {
67
+ const text = await response.text().catch(() => '');
68
+ throw new Error(`Voyage rerank failed (${response.status}): ${text || response.statusText}`);
69
+ }
70
+ const json = await response.json();
71
+ this.lastTokensUsed = json.usage?.total_tokens ?? 0;
72
+ return json.data.map((r, idx) => ({
73
+ id: params.documents[r.index].id,
74
+ relevanceScore: r.relevance_score,
75
+ document: params.documents[r.index],
76
+ rank: idx,
77
+ }));
78
+ }
79
+ }
80
+ /**
81
+ * Voyage `rerank-2` re-ranker. Configure per scope via
82
+ * `SearchScope.ScopeConfig.reRanker.driverClass = 'VoyageReRanker'`.
83
+ *
84
+ * Override the model via `ScopeConfig.reRanker.config.model = 'rerank-2-lite'` for
85
+ * the cheaper lite variant.
86
+ */
87
+ let VoyageReRanker = class VoyageReRanker extends BaseReRanker {
88
+ constructor() {
89
+ super(...arguments);
90
+ /**
91
+ * Per-instance reranker, captured so we can read `lastTokensUsed` after a call to
92
+ * report exact cost. Recreated per ReRank invocation when config or contextUser
93
+ * change shouldn't be a concern in practice.
94
+ */
95
+ this.currentAIReranker = null;
96
+ }
97
+ static { VoyageReRanker_1 = this; }
98
+ /** Pricing in cents-per-token, indexed by model name. As of 2026-04. */
99
+ static { this.CENTS_PER_TOKEN = {
100
+ 'rerank-2': 0.05 / 1_000_000 * 100, // $0.05 / 1M tokens → 5e-6 cents/token
101
+ 'rerank-2-lite': 0.02 / 1_000_000 * 100, // $0.02 / 1M tokens → 2e-6 cents/token
102
+ }; }
103
+ get DriverClass() {
104
+ return 'VoyageReRanker';
105
+ }
106
+ get Name() {
107
+ return 'Voyage';
108
+ }
109
+ get Version() {
110
+ return '1';
111
+ }
112
+ /** Voyage's published cap is 1000 documents per call. */
113
+ GetMaxResultCount() {
114
+ return 1000;
115
+ }
116
+ /**
117
+ * Pre-call cost estimate using `ceil(charCount / 4)` per document plus query
118
+ * tokens. Conservative — tends to over-estimate, which is what a budget guard
119
+ * wants. Default model is `rerank-2`; pass `config.model` to switch.
120
+ */
121
+ EstimateCostCents(resultCount) {
122
+ // Use the default model's pricing for the estimate; the actual call will use
123
+ // whatever's in config.model and the post-call cost report will be exact.
124
+ const pricePerToken = VoyageReRanker_1.CENTS_PER_TOKEN['rerank-2'] ?? 0;
125
+ // Assume average document size ~250 chars (~62 tokens) and a 30-token query.
126
+ const approxTokens = resultCount * 62 + 30;
127
+ return approxTokens * pricePerToken;
128
+ }
129
+ getAIReranker(config, _contextUser) {
130
+ // Return the cached instance if `ReRank()` already constructed one for this
131
+ // invocation — super.ReRank calls us a second time during dispatch. Without
132
+ // the cache we'd build two separate Voyage clients per call and lose track of
133
+ // `lastTokensUsed`.
134
+ if (this.currentAIReranker)
135
+ return this.currentAIReranker;
136
+ const apiKey = GetAIAPIKey('VoyageLLM');
137
+ if (!apiKey) {
138
+ LogError('VoyageReRanker: no API key found for driver "VoyageLLM" — set AI_VENDOR_API_KEY__VOYAGELLM or register an AIAPIKeys subclass.');
139
+ return null;
140
+ }
141
+ const modelName = config?.['model'] ?? 'rerank-2';
142
+ this.currentAIReranker = new VoyageScopeAIReranker(apiKey, modelName);
143
+ return this.currentAIReranker;
144
+ }
145
+ /**
146
+ * Override `ReRank` to report exact post-call cost via `usage.total_tokens` from
147
+ * the Voyage response, not the pre-call estimate. The estimate exists only to feed
148
+ * the budget guard's pre-call short-circuit.
149
+ */
150
+ async ReRank(query, candidates, topN, contextUser, config) {
151
+ if (topN <= 0 || candidates.length === 0)
152
+ return [];
153
+ // Reset the per-call cache and prime it via getAIReranker. Super will reuse it.
154
+ this.currentAIReranker = null;
155
+ const apiReranker = this.getAIReranker(config, contextUser);
156
+ try {
157
+ if (!apiReranker) {
158
+ // No API key — fall through to a top-N slice without charge.
159
+ return candidates.slice(0, topN);
160
+ }
161
+ const result = await super.ReRank(query, candidates, topN, contextUser, config);
162
+ const modelName = config?.['model'] ?? 'rerank-2';
163
+ const pricePerToken = VoyageReRanker_1.CENTS_PER_TOKEN[modelName] ?? VoyageReRanker_1.CENTS_PER_TOKEN['rerank-2'];
164
+ const cents = apiReranker.lastTokensUsed * pricePerToken;
165
+ if (cents > 0) {
166
+ this.reportCost(cents);
167
+ }
168
+ return result;
169
+ }
170
+ finally {
171
+ this.currentAIReranker = null;
172
+ }
173
+ }
174
+ };
175
+ VoyageReRanker = VoyageReRanker_1 = __decorate([
176
+ RegisterClass(BaseReRanker, 'VoyageReRanker')
177
+ ], VoyageReRanker);
178
+ export { VoyageReRanker };
179
+ /** Tree-shake prevention helper. */
180
+ export function LoadVoyageReRanker() {
181
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
182
+ const _ref = VoyageReRanker;
183
+ }
184
+ //# sourceMappingURL=VoyageReRanker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VoyageReRanker.js","sourceRoot":"","sources":["../../src/rerankers/VoyageReRanker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;;;;;;;;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAY,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EACH,YAAY,IAAI,cAAc,EAG9B,WAAW,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAGvD,MAAM,iBAAiB,GAAG,oCAAoC,CAAC;AAa/D;;;;GAIG;AACH,SAAS,cAAc,CAAC,IAAY;IAChC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;GAEG;AACH,MAAM,qBAAsB,SAAQ,cAAc;IAO9C,YAAY,MAAc,EAAE,SAAiB;QACzC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAP7B;;;WAGG;QACI,mBAAc,GAAG,CAAC,CAAC;IAI1B,CAAC;IAES,KAAK,CAAC,QAAQ,CAAC,MAAoB;QACzC,MAAM,IAAI,GAAG;YACT,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5C,KAAK,EAAE,IAAI,CAAC,UAAU;YACtB,KAAK,EAAE,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM;YAC7C,gBAAgB,EAAE,KAAK;SAC1B,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,iBAAiB,EAAE;YAC5C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;gBACxC,cAAc,EAAE,kBAAkB;aACrC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC7B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,MAAM,MAAM,IAAI,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACjG,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA0B,CAAC;QAC3D,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC;QAEpD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YAC9B,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE;YAChC,cAAc,EAAE,CAAC,CAAC,eAAe;YACjC,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;YACnC,IAAI,EAAE,GAAG;SACZ,CAAC,CAAC,CAAC;IACR,CAAC;CACJ;AAED;;;;;;GAMG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,YAAY;IAAzC;;QAOH;;;;WAIG;QACK,sBAAiB,GAAiC,IAAI,CAAC;IAoFnE,CAAC;;IA/FG,wEAAwE;aAChD,oBAAe,GAA2B;QAC9D,UAAU,EAAE,IAAI,GAAG,SAAS,GAAG,GAAG,EAAS,uCAAuC;QAClF,eAAe,EAAE,IAAI,GAAG,SAAS,GAAG,GAAG,EAAI,uCAAuC;KACrF,AAHsC,CAGrC;IASF,IAAW,WAAW;QAClB,OAAO,gBAAgB,CAAC;IAC5B,CAAC;IACD,IAAoB,IAAI;QACpB,OAAO,QAAQ,CAAC;IACpB,CAAC;IACD,IAAoB,OAAO;QACvB,OAAO,GAAG,CAAC;IACf,CAAC;IAED,yDAAyD;IACzC,iBAAiB;QAC7B,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACa,iBAAiB,CAAC,WAAmB;QACjD,6EAA6E;QAC7E,0EAA0E;QAC1E,MAAM,aAAa,GAAG,gBAAc,CAAC,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtE,6EAA6E;QAC7E,MAAM,YAAY,GAAG,WAAW,GAAG,EAAE,GAAG,EAAE,CAAC;QAC3C,OAAO,YAAY,GAAG,aAAa,CAAC;IACxC,CAAC;IAEkB,aAAa,CAC5B,MAA2C,EAC3C,YAAsB;QAEtB,4EAA4E;QAC5E,4EAA4E;QAC5E,8EAA8E;QAC9E,oBAAoB;QACpB,IAAI,IAAI,CAAC,iBAAiB;YAAE,OAAO,IAAI,CAAC,iBAAiB,CAAC;QAE1D,MAAM,MAAM,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,QAAQ,CAAC,+HAA+H,CAAC,CAAC;YAC1I,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,MAAM,SAAS,GAAI,MAAM,EAAE,CAAC,OAAO,CAAwB,IAAI,UAAU,CAAC;QAC1E,IAAI,CAAC,iBAAiB,GAAG,IAAI,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACa,KAAK,CAAC,MAAM,CACxB,KAAa,EACb,UAA8B,EAC9B,IAAY,EACZ,WAAqB,EACrB,MAAgC;QAEhC,IAAI,IAAI,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACpD,gFAAgF;QAChF,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC9B,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,WAAW,CAAiC,CAAC;QAC5F,IAAI,CAAC;YACD,IAAI,CAAC,WAAW,EAAE,CAAC;gBACf,6DAA6D;gBAC7D,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;YACrC,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;YAChF,MAAM,SAAS,GAAI,MAAM,EAAE,CAAC,OAAO,CAAwB,IAAI,UAAU,CAAC;YAC1E,MAAM,aAAa,GAAG,gBAAc,CAAC,eAAe,CAAC,SAAS,CAAC,IAAI,gBAAc,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YAC9G,MAAM,KAAK,GAAG,WAAW,CAAC,cAAc,GAAG,aAAa,CAAC;YACzD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACZ,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;YACD,OAAO,MAAM,CAAC;QAClB,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAClC,CAAC;IACL,CAAC;;AA/FQ,cAAc;IAD1B,aAAa,CAAC,YAAY,EAAE,gBAAgB,CAAC;GACjC,cAAc,CAgG1B;;AAED,oCAAoC;AACpC,MAAM,UAAU,kBAAkB;IAC9B,6DAA6D;IAC7D,MAAM,IAAI,GAAG,cAAc,CAAC;AAChC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/search-engine",
3
3
  "type": "module",
4
- "version": "5.33.0",
4
+ "version": "5.34.1",
5
5
  "description": "MemberJunction: Reusable server-side search engine - vector + full-text + entity search with RRF fusion",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -16,16 +16,21 @@
16
16
  "author": "MemberJunction.com",
17
17
  "license": "ISC",
18
18
  "dependencies": {
19
- "@memberjunction/ai": "5.33.0",
20
- "@memberjunction/ai-vectordb": "5.33.0",
21
- "@memberjunction/aiengine": "5.33.0",
22
- "@memberjunction/core": "5.33.0",
23
- "@memberjunction/core-entities": "5.33.0",
24
- "@memberjunction/global": "5.33.0",
25
- "@memberjunction/storage": "5.33.0"
19
+ "@memberjunction/ai": "5.34.1",
20
+ "@memberjunction/ai-vectordb": "5.34.1",
21
+ "@memberjunction/aiengine": "5.34.1",
22
+ "@memberjunction/core": "5.34.1",
23
+ "@memberjunction/core-entities": "5.34.1",
24
+ "@memberjunction/global": "5.34.1",
25
+ "@memberjunction/storage": "5.34.1",
26
+ "nunjucks": "^3.2.4"
27
+ },
28
+ "optionalDependencies": {
29
+ "cohere-ai": "^7.20.0"
26
30
  },
27
31
  "devDependencies": {
28
32
  "@types/node": "24.10.11",
33
+ "@types/nunjucks": "^3.2.6",
29
34
  "typescript": "^5.9.3"
30
35
  },
31
36
  "repository": {