@exulu/backend 1.46.0 → 1.47.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 (151) hide show
  1. package/.agents/skills/mintlify/SKILL.md +347 -0
  2. package/.editorconfig +15 -0
  3. package/.eslintrc.json +52 -0
  4. package/.jscpd.json +18 -0
  5. package/.prettierignore +5 -0
  6. package/.prettierrc.json +12 -0
  7. package/CHANGELOG.md +11 -4
  8. package/README.md +747 -0
  9. package/SECURITY.md +5 -0
  10. package/dist/index.cjs +12015 -10506
  11. package/dist/index.d.cts +725 -667
  12. package/dist/index.d.ts +725 -667
  13. package/dist/index.js +12034 -10518
  14. package/ee/LICENSE.md +62 -0
  15. package/ee/agentic-retrieval/index.ts +1109 -0
  16. package/ee/documents/THIRD_PARTY_LICENSES/docling.txt +31 -0
  17. package/ee/documents/processing/build_pdf_processor.sh +35 -0
  18. package/ee/documents/processing/chunk_markdown.py +263 -0
  19. package/ee/documents/processing/doc_processor.ts +635 -0
  20. package/ee/documents/processing/pdf_processor.spec +115 -0
  21. package/ee/documents/processing/pdf_to_markdown.py +420 -0
  22. package/ee/documents/processing/requirements.txt +4 -0
  23. package/ee/entitlements.ts +49 -0
  24. package/ee/markdown.ts +686 -0
  25. package/ee/queues/decorator.ts +140 -0
  26. package/ee/queues/queues.ts +156 -0
  27. package/ee/queues/server.ts +6 -0
  28. package/ee/rbac-resolver.ts +51 -0
  29. package/ee/rbac-update.ts +111 -0
  30. package/ee/schemas.ts +347 -0
  31. package/ee/tokenizer.ts +80 -0
  32. package/ee/workers.ts +1423 -0
  33. package/eslint.config.js +88 -0
  34. package/jest.config.ts +25 -0
  35. package/license.md +73 -49
  36. package/mintlify-docs/.mintignore +7 -0
  37. package/mintlify-docs/AGENTS.md +33 -0
  38. package/mintlify-docs/CLAUDE.MD +50 -0
  39. package/mintlify-docs/CONTRIBUTING.md +32 -0
  40. package/mintlify-docs/LICENSE +21 -0
  41. package/mintlify-docs/README.md +55 -0
  42. package/mintlify-docs/ai-tools/claude-code.mdx +43 -0
  43. package/mintlify-docs/ai-tools/cursor.mdx +39 -0
  44. package/mintlify-docs/ai-tools/windsurf.mdx +39 -0
  45. package/mintlify-docs/api-reference/core-types/agent-types.mdx +110 -0
  46. package/mintlify-docs/api-reference/core-types/analytics-types.mdx +95 -0
  47. package/mintlify-docs/api-reference/core-types/configuration-types.mdx +83 -0
  48. package/mintlify-docs/api-reference/core-types/evaluation-types.mdx +106 -0
  49. package/mintlify-docs/api-reference/core-types/job-types.mdx +135 -0
  50. package/mintlify-docs/api-reference/core-types/overview.mdx +73 -0
  51. package/mintlify-docs/api-reference/core-types/prompt-types.mdx +102 -0
  52. package/mintlify-docs/api-reference/core-types/rbac-types.mdx +163 -0
  53. package/mintlify-docs/api-reference/core-types/session-types.mdx +77 -0
  54. package/mintlify-docs/api-reference/core-types/user-management.mdx +112 -0
  55. package/mintlify-docs/api-reference/core-types/workflow-types.mdx +88 -0
  56. package/mintlify-docs/api-reference/core-types.mdx +585 -0
  57. package/mintlify-docs/api-reference/dynamic-types.mdx +851 -0
  58. package/mintlify-docs/api-reference/endpoint/create.mdx +4 -0
  59. package/mintlify-docs/api-reference/endpoint/delete.mdx +4 -0
  60. package/mintlify-docs/api-reference/endpoint/get.mdx +4 -0
  61. package/mintlify-docs/api-reference/endpoint/webhook.mdx +4 -0
  62. package/mintlify-docs/api-reference/introduction.mdx +661 -0
  63. package/mintlify-docs/api-reference/mutations.mdx +1012 -0
  64. package/mintlify-docs/api-reference/openapi.json +217 -0
  65. package/mintlify-docs/api-reference/queries.mdx +1154 -0
  66. package/mintlify-docs/backend/introduction.mdx +218 -0
  67. package/mintlify-docs/changelog.mdx +293 -0
  68. package/mintlify-docs/community-edition.mdx +304 -0
  69. package/mintlify-docs/core/exulu-agent/api-reference.mdx +894 -0
  70. package/mintlify-docs/core/exulu-agent/configuration.mdx +690 -0
  71. package/mintlify-docs/core/exulu-agent/introduction.mdx +552 -0
  72. package/mintlify-docs/core/exulu-app/api-reference.mdx +481 -0
  73. package/mintlify-docs/core/exulu-app/configuration.mdx +319 -0
  74. package/mintlify-docs/core/exulu-app/introduction.mdx +117 -0
  75. package/mintlify-docs/core/exulu-authentication.mdx +810 -0
  76. package/mintlify-docs/core/exulu-chunkers/api-reference.mdx +1011 -0
  77. package/mintlify-docs/core/exulu-chunkers/configuration.mdx +596 -0
  78. package/mintlify-docs/core/exulu-chunkers/introduction.mdx +403 -0
  79. package/mintlify-docs/core/exulu-context/api-reference.mdx +911 -0
  80. package/mintlify-docs/core/exulu-context/configuration.mdx +648 -0
  81. package/mintlify-docs/core/exulu-context/introduction.mdx +394 -0
  82. package/mintlify-docs/core/exulu-database.mdx +811 -0
  83. package/mintlify-docs/core/exulu-default-agents.mdx +545 -0
  84. package/mintlify-docs/core/exulu-eval/api-reference.mdx +772 -0
  85. package/mintlify-docs/core/exulu-eval/configuration.mdx +680 -0
  86. package/mintlify-docs/core/exulu-eval/introduction.mdx +459 -0
  87. package/mintlify-docs/core/exulu-logging.mdx +464 -0
  88. package/mintlify-docs/core/exulu-otel.mdx +670 -0
  89. package/mintlify-docs/core/exulu-queues/api-reference.mdx +648 -0
  90. package/mintlify-docs/core/exulu-queues/configuration.mdx +650 -0
  91. package/mintlify-docs/core/exulu-queues/introduction.mdx +474 -0
  92. package/mintlify-docs/core/exulu-reranker/api-reference.mdx +630 -0
  93. package/mintlify-docs/core/exulu-reranker/configuration.mdx +663 -0
  94. package/mintlify-docs/core/exulu-reranker/introduction.mdx +516 -0
  95. package/mintlify-docs/core/exulu-tool/api-reference.mdx +723 -0
  96. package/mintlify-docs/core/exulu-tool/configuration.mdx +805 -0
  97. package/mintlify-docs/core/exulu-tool/introduction.mdx +539 -0
  98. package/mintlify-docs/core/exulu-variables/api-reference.mdx +699 -0
  99. package/mintlify-docs/core/exulu-variables/configuration.mdx +736 -0
  100. package/mintlify-docs/core/exulu-variables/introduction.mdx +511 -0
  101. package/mintlify-docs/development.mdx +94 -0
  102. package/mintlify-docs/docs.json +248 -0
  103. package/mintlify-docs/enterprise-edition.mdx +538 -0
  104. package/mintlify-docs/essentials/code.mdx +35 -0
  105. package/mintlify-docs/essentials/images.mdx +59 -0
  106. package/mintlify-docs/essentials/markdown.mdx +88 -0
  107. package/mintlify-docs/essentials/navigation.mdx +87 -0
  108. package/mintlify-docs/essentials/reusable-snippets.mdx +110 -0
  109. package/mintlify-docs/essentials/settings.mdx +318 -0
  110. package/mintlify-docs/favicon.svg +3 -0
  111. package/mintlify-docs/frontend/introduction.mdx +39 -0
  112. package/mintlify-docs/getting-started.mdx +267 -0
  113. package/mintlify-docs/guides/custom-agent.mdx +608 -0
  114. package/mintlify-docs/guides/first-agent.mdx +315 -0
  115. package/mintlify-docs/images/admin_ui.png +0 -0
  116. package/mintlify-docs/images/contexts.png +0 -0
  117. package/mintlify-docs/images/create_agents.png +0 -0
  118. package/mintlify-docs/images/evals.png +0 -0
  119. package/mintlify-docs/images/graphql.png +0 -0
  120. package/mintlify-docs/images/graphql_api.png +0 -0
  121. package/mintlify-docs/images/hero-dark.png +0 -0
  122. package/mintlify-docs/images/hero-light.png +0 -0
  123. package/mintlify-docs/images/hero.png +0 -0
  124. package/mintlify-docs/images/knowledge_sources.png +0 -0
  125. package/mintlify-docs/images/mcp.png +0 -0
  126. package/mintlify-docs/images/scaling.png +0 -0
  127. package/mintlify-docs/index.mdx +411 -0
  128. package/mintlify-docs/logo/dark.svg +9 -0
  129. package/mintlify-docs/logo/light.svg +9 -0
  130. package/mintlify-docs/partners.mdx +558 -0
  131. package/mintlify-docs/products.mdx +77 -0
  132. package/mintlify-docs/snippets/snippet-intro.mdx +4 -0
  133. package/mintlify-docs/styles.css +207 -0
  134. package/{documentation → old-documentation}/logging.md +3 -3
  135. package/package.json +35 -4
  136. package/skills-lock.json +10 -0
  137. package/types/context-processor.ts +45 -0
  138. package/types/exulu-table-definition.ts +79 -0
  139. package/types/file-types.ts +18 -0
  140. package/types/models/agent.ts +10 -12
  141. package/types/models/exulu-agent-tool-config.ts +11 -0
  142. package/types/models/rate-limiter-rules.ts +7 -0
  143. package/types/provider-config.ts +21 -0
  144. package/types/queue-config.ts +16 -0
  145. package/types/rbac-rights-modes.ts +1 -0
  146. package/types/statistics.ts +20 -0
  147. package/types/workflow.ts +31 -0
  148. package/changelog-backend-10.11.2025_03.12.2025.md +0 -316
  149. package/types/models/agent-backend.ts +0 -15
  150. /package/{documentation → old-documentation}/otel.md +0 -0
  151. /package/{patch-older-releases-readme.md → old-documentation/patch-older-releases.md} +0 -0
@@ -0,0 +1,516 @@
1
+ ---
2
+ title: "Overview"
3
+ description: "Improve search result relevance by reordering chunks with specialized reranking models"
4
+ ---
5
+
6
+ ## Overview
7
+
8
+ `ExuluReranker` is a component that improves search result quality by reordering chunks based on relevance to the user's query. After initial vector or hybrid search retrieves candidate results, a reranker applies more sophisticated relevance scoring to surface the most useful chunks at the top.
9
+
10
+ ## Key features
11
+
12
+ <CardGroup cols={2}>
13
+ <Card title="Post-search refinement" icon="arrow-up-arrow-down">
14
+ Reorder search results after initial retrieval for better relevance
15
+ </Card>
16
+ <Card title="Model-agnostic" icon="wand-magic-sparkles">
17
+ Use any reranking API (Cohere, Voyage AI, custom models)
18
+ </Card>
19
+ <Card title="Simple integration" icon="plug">
20
+ Drop-in enhancement for ExuluContext search
21
+ </Card>
22
+ <Card title="Flexible scoring" icon="sliders">
23
+ Custom reranking logic or third-party services
24
+ </Card>
25
+ </CardGroup>
26
+
27
+ ## What is a reranker?
28
+
29
+ A reranker is a specialized model or algorithm that:
30
+
31
+ 1. **Receives initial search results** from vector or hybrid search
32
+ 2. **Analyzes query-document pairs** to compute refined relevance scores
33
+ 3. **Reorders the results** to surface the most relevant chunks first
34
+ 4. **Returns the reordered chunks** to the application
35
+
36
+ Rerankers typically use cross-attention mechanisms or more sophisticated language models than embedding models, providing better relevance at the cost of higher latency. This makes them ideal for post-processing search results.
37
+
38
+ ## Why use reranking?
39
+
40
+ <AccordionGroup>
41
+ <Accordion title="Better relevance">
42
+ Reranking models analyze the relationship between query and document more deeply than embedding similarity, leading to more accurate results.
43
+ </Accordion>
44
+
45
+ <Accordion title="Two-stage retrieval">
46
+ Fast vector search retrieves candidates, then slower but more accurate reranking refines the top results. This balances speed and quality.
47
+ </Accordion>
48
+
49
+ <Accordion title="Domain-specific ranking">
50
+ Custom rerankers can implement business logic, user preferences, or domain-specific relevance criteria.
51
+ </Accordion>
52
+
53
+ <Accordion title="Handle semantic nuances">
54
+ Rerankers can better understand negations, comparisons, and complex queries that embedding models might miss.
55
+ </Accordion>
56
+ </AccordionGroup>
57
+
58
+ ## Quick start
59
+
60
+ ```typescript
61
+ import { ExuluReranker } from "@exulu/backend";
62
+
63
+ // Create a reranker using Cohere
64
+ const cohereReranker = new ExuluReranker({
65
+ id: "cohere_reranker",
66
+ name: "Cohere Rerank",
67
+ description: "Reranks search results using Cohere's rerank-english-v3.0 model",
68
+ execute: async ({ query, chunks }) => {
69
+ const response = await fetch("https://api.cohere.com/v1/rerank", {
70
+ method: "POST",
71
+ headers: {
72
+ "Authorization": `Bearer ${process.env.COHERE_API_KEY}`,
73
+ "Content-Type": "application/json"
74
+ },
75
+ body: JSON.stringify({
76
+ model: "rerank-english-v3.0",
77
+ query: query,
78
+ documents: chunks.map(chunk => chunk.chunk_content),
79
+ top_n: 10,
80
+ return_documents: false
81
+ })
82
+ });
83
+
84
+ const data = await response.json();
85
+
86
+ // Reorder chunks based on Cohere's ranking
87
+ return data.results
88
+ .sort((a, b) => b.relevance_score - a.relevance_score)
89
+ .map(result => chunks[result.index]);
90
+ }
91
+ });
92
+
93
+ // Use with ExuluContext
94
+ const docsContext = new ExuluContext({
95
+ id: "documentation",
96
+ name: "Documentation",
97
+ description: "Product docs",
98
+ active: true,
99
+ fields: [/* ... */],
100
+ sources: [],
101
+ resultReranker: async (results) => {
102
+ return cohereReranker.run(
103
+ results[0]?.query || "",
104
+ results
105
+ );
106
+ }
107
+ });
108
+ ```
109
+
110
+ ## Architecture
111
+
112
+ ### Integration with ExuluContext
113
+
114
+ Rerankers integrate with ExuluContext through the `resultReranker` configuration:
115
+
116
+ ```typescript
117
+ const context = new ExuluContext({
118
+ // ... other config
119
+ resultReranker: async (chunks) => {
120
+ // Access the query from the first chunk's context
121
+ const query = chunks[0]?.context?.query || "";
122
+
123
+ // Apply reranking
124
+ return reranker.run(query, chunks);
125
+ }
126
+ });
127
+ ```
128
+
129
+ When a search is performed:
130
+ 1. Vector/hybrid search retrieves initial candidates
131
+ 2. `resultReranker` is called with the chunks
132
+ 3. Reranker reorders the chunks
133
+ 4. Reordered results are returned to the agent/user
134
+
135
+ ### Chunk structure
136
+
137
+ Rerankers work with `VectorSearchChunkResult` objects:
138
+
139
+ ```typescript
140
+ type VectorSearchChunkResult = {
141
+ chunk_content: string; // The text content
142
+ chunk_index: number; // Position in original document
143
+ chunk_id: string; // Unique chunk ID
144
+ chunk_source: string; // Source item ID
145
+ chunk_metadata: Record<string, string>;
146
+ chunk_created_at: string;
147
+ chunk_updated_at: string;
148
+ item_id: string; // Parent item ID
149
+ item_external_id: string;
150
+ item_name: string; // Parent item name
151
+ item_updated_at: string;
152
+ item_created_at: string;
153
+ chunk_cosine_distance?: number; // Similarity score
154
+ chunk_fts_rank?: number; // Keyword score
155
+ chunk_hybrid_score?: number; // Combined score
156
+ context?: {
157
+ name: string;
158
+ id: string;
159
+ };
160
+ };
161
+ ```
162
+
163
+ ## Common reranking strategies
164
+
165
+ <Tabs>
166
+ <Tab title="API-based reranking">
167
+ Use third-party reranking APIs like Cohere, Voyage AI, or Jina AI:
168
+
169
+ ```typescript
170
+ const apiReranker = new ExuluReranker({
171
+ id: "cohere_rerank",
172
+ name: "Cohere Reranker",
173
+ description: "Uses Cohere rerank API",
174
+ execute: async ({ query, chunks }) => {
175
+ const response = await fetch("https://api.cohere.com/v1/rerank", {
176
+ method: "POST",
177
+ headers: {
178
+ "Authorization": `Bearer ${API_KEY}`,
179
+ "Content-Type": "application/json"
180
+ },
181
+ body: JSON.stringify({
182
+ model: "rerank-english-v3.0",
183
+ query,
184
+ documents: chunks.map(c => c.chunk_content),
185
+ top_n: chunks.length
186
+ })
187
+ });
188
+
189
+ const data = await response.json();
190
+ return data.results.map(r => chunks[r.index]);
191
+ }
192
+ });
193
+ ```
194
+ </Tab>
195
+
196
+ <Tab title="Custom scoring">
197
+ Implement custom business logic:
198
+
199
+ ```typescript
200
+ const customReranker = new ExuluReranker({
201
+ id: "custom_scorer",
202
+ name: "Custom Scorer",
203
+ description: "Custom relevance scoring with business rules",
204
+ execute: async ({ query, chunks }) => {
205
+ // Score each chunk
206
+ const scored = chunks.map(chunk => ({
207
+ chunk,
208
+ score: computeCustomScore(query, chunk)
209
+ }));
210
+
211
+ // Sort by score descending
212
+ scored.sort((a, b) => b.score - a.score);
213
+
214
+ return scored.map(s => s.chunk);
215
+ }
216
+ });
217
+
218
+ function computeCustomScore(query: string, chunk: VectorSearchChunkResult): number {
219
+ let score = chunk.chunk_hybrid_score || 0;
220
+
221
+ // Boost recent content
222
+ const age = Date.now() - new Date(chunk.chunk_updated_at).getTime();
223
+ const daysSinceUpdate = age / (1000 * 60 * 60 * 24);
224
+ score += Math.max(0, 1 - daysSinceUpdate / 365);
225
+
226
+ // Boost specific categories
227
+ if (chunk.chunk_metadata.category === "tutorial") {
228
+ score *= 1.2;
229
+ }
230
+
231
+ // Penalize very short chunks
232
+ if (chunk.chunk_content.length < 100) {
233
+ score *= 0.7;
234
+ }
235
+
236
+ return score;
237
+ }
238
+ ```
239
+ </Tab>
240
+
241
+ <Tab title="LLM-based reranking">
242
+ Use an LLM to judge relevance:
243
+
244
+ ```typescript
245
+ const llmReranker = new ExuluReranker({
246
+ id: "llm_rerank",
247
+ name: "LLM Reranker",
248
+ description: "Uses GPT-4 to score relevance",
249
+ execute: async ({ query, chunks }) => {
250
+ // Score chunks in parallel
251
+ const scoredPromises = chunks.map(async (chunk) => {
252
+ const prompt = `On a scale of 0-10, how relevant is this passage to the query?
253
+
254
+ Query: "${query}"
255
+
256
+ Passage: "${chunk.chunk_content}"
257
+
258
+ Respond with only a number.`;
259
+
260
+ const response = await openai.chat.completions.create({
261
+ model: "gpt-4o-mini",
262
+ messages: [{ role: "user", content: prompt }],
263
+ temperature: 0
264
+ });
265
+
266
+ const score = parseFloat(response.choices[0].message.content);
267
+
268
+ return { chunk, score };
269
+ });
270
+
271
+ const scored = await Promise.all(scoredPromises);
272
+ scored.sort((a, b) => b.score - a.score);
273
+
274
+ return scored.map(s => s.chunk);
275
+ }
276
+ });
277
+ ```
278
+
279
+ <Warning>
280
+ LLM-based reranking can be expensive and slow. Use for small result sets or where quality is critical.
281
+ </Warning>
282
+ </Tab>
283
+
284
+ <Tab title="Hybrid approach">
285
+ Combine multiple signals:
286
+
287
+ ```typescript
288
+ const hybridReranker = new ExuluReranker({
289
+ id: "hybrid_rerank",
290
+ name: "Hybrid Reranker",
291
+ description: "Combines API reranking with custom scoring",
292
+ execute: async ({ query, chunks }) => {
293
+ // Step 1: Get API reranking scores
294
+ const apiResponse = await fetch("https://api.cohere.com/v1/rerank", {
295
+ method: "POST",
296
+ headers: {
297
+ "Authorization": `Bearer ${API_KEY}`,
298
+ "Content-Type": "application/json"
299
+ },
300
+ body: JSON.stringify({
301
+ model: "rerank-english-v3.0",
302
+ query,
303
+ documents: chunks.map(c => c.chunk_content)
304
+ })
305
+ });
306
+
307
+ const apiData = await apiResponse.json();
308
+
309
+ // Step 2: Combine with custom business logic
310
+ const scored = chunks.map((chunk, idx) => {
311
+ const apiScore = apiData.results.find(r => r.index === idx)?.relevance_score || 0;
312
+ const customScore = computeCustomScore(chunk);
313
+
314
+ return {
315
+ chunk,
316
+ score: (apiScore * 0.7) + (customScore * 0.3) // Weighted combination
317
+ };
318
+ });
319
+
320
+ scored.sort((a, b) => b.score - a.score);
321
+ return scored.map(s => s.chunk);
322
+ }
323
+ });
324
+ ```
325
+ </Tab>
326
+ </Tabs>
327
+
328
+ ## Usage patterns
329
+
330
+ ### With ExuluContext
331
+
332
+ The most common pattern is integrating with ExuluContext:
333
+
334
+ ```typescript
335
+ import { ExuluContext, ExuluReranker } from "@exulu/backend";
336
+
337
+ // Create reranker
338
+ const reranker = new ExuluReranker({
339
+ id: "my_reranker",
340
+ name: "My Reranker",
341
+ description: "Custom reranking logic",
342
+ execute: async ({ query, chunks }) => {
343
+ // Your reranking logic
344
+ return reorderedChunks;
345
+ }
346
+ });
347
+
348
+ // Use with context
349
+ const context = new ExuluContext({
350
+ id: "docs",
351
+ name: "Documentation",
352
+ // ... other config
353
+ resultReranker: async (chunks) => {
354
+ const query = chunks[0]?.context?.query || "";
355
+ return reranker.run(query, chunks);
356
+ }
357
+ });
358
+ ```
359
+
360
+ ### Standalone usage
361
+
362
+ You can also use rerankers independently:
363
+
364
+ ```typescript
365
+ const chunks = await context.search({
366
+ query: "How do I authenticate?",
367
+ method: "hybridSearch",
368
+ // ... other params
369
+ });
370
+
371
+ const rerankedChunks = await reranker.run(
372
+ "How do I authenticate?",
373
+ chunks.chunks
374
+ );
375
+
376
+ console.log(rerankedChunks[0].chunk_content); // Most relevant result
377
+ ```
378
+
379
+ ### Conditional reranking
380
+
381
+ Apply reranking only when needed:
382
+
383
+ ```typescript
384
+ resultReranker: async (chunks) => {
385
+ // Only rerank if we have enough results
386
+ if (chunks.length < 3) {
387
+ return chunks;
388
+ }
389
+
390
+ // Only rerank if scores are similar (ambiguous results)
391
+ const scores = chunks
392
+ .map(c => c.chunk_hybrid_score || 0)
393
+ .filter(s => s > 0);
394
+
395
+ if (scores.length === 0) return chunks;
396
+
397
+ const avgScore = scores.reduce((a, b) => a + b) / scores.length;
398
+ const variance = scores.reduce((sum, score) => sum + Math.pow(score - avgScore, 2), 0) / scores.length;
399
+
400
+ // Low variance means clear ranking, skip reranking
401
+ if (variance < 0.01) {
402
+ return chunks;
403
+ }
404
+
405
+ // High variance, apply reranking
406
+ const query = chunks[0]?.context?.query || "";
407
+ return reranker.run(query, chunks);
408
+ }
409
+ ```
410
+
411
+ ## Popular reranking services
412
+
413
+ <CardGroup cols={2}>
414
+ <Card title="Cohere Rerank" icon="bolt" href="https://cohere.com/rerank">
415
+ High-quality reranking with multilingual support
416
+ </Card>
417
+ <Card title="Voyage AI" icon="ship" href="https://www.voyageai.com/">
418
+ Domain-specific reranking models
419
+ </Card>
420
+ <Card title="Jina AI" icon="cube" href="https://jina.ai/reranker/">
421
+ Open-source and API-based reranking
422
+ </Card>
423
+ <Card title="Custom models" icon="brain">
424
+ Self-hosted models like cross-encoders or custom LLM scoring
425
+ </Card>
426
+ </CardGroup>
427
+
428
+ ## Best practices
429
+
430
+ <Tip>
431
+ **Rerank the right amount**: Reranking is slower than initial retrieval. Retrieve more candidates (e.g., 50-100) with fast vector search, then rerank the top 10-20.
432
+ </Tip>
433
+
434
+ <Note>
435
+ **Preserve metadata**: Ensure your reranker returns chunks with all their original metadata intact. Only change the order, not the content.
436
+ </Note>
437
+
438
+ <Warning>
439
+ **Watch latency**: Reranking adds latency to search. Test with production query volumes and consider caching or async reranking for non-critical queries.
440
+ </Warning>
441
+
442
+ <Info>
443
+ **Measure impact**: A/B test your reranker to ensure it improves relevance. Track metrics like click-through rate or user satisfaction.
444
+ </Info>
445
+
446
+ ## Performance considerations
447
+
448
+ <AccordionGroup>
449
+ <Accordion title="Batch reranking">
450
+ Some APIs support batch reranking for better throughput:
451
+
452
+ ```typescript
453
+ execute: async ({ query, chunks }) => {
454
+ // Send all documents at once
455
+ const response = await cohereClient.rerank({
456
+ model: "rerank-english-v3.0",
457
+ query,
458
+ documents: chunks.map(c => c.chunk_content),
459
+ top_n: 20 // Limit results
460
+ });
461
+
462
+ return response.results.map(r => chunks[r.index]);
463
+ }
464
+ ```
465
+ </Accordion>
466
+
467
+ <Accordion title="Caching">
468
+ Cache reranking results for repeated queries:
469
+
470
+ ```typescript
471
+ const cache = new Map();
472
+
473
+ execute: async ({ query, chunks }) => {
474
+ const cacheKey = `${query}:${chunks.map(c => c.chunk_id).join(",")}`;
475
+
476
+ if (cache.has(cacheKey)) {
477
+ return cache.get(cacheKey);
478
+ }
479
+
480
+ const reranked = await performReranking(query, chunks);
481
+ cache.set(cacheKey, reranked);
482
+
483
+ return reranked;
484
+ }
485
+ ```
486
+ </Accordion>
487
+
488
+ <Accordion title="Parallel processing">
489
+ For custom scoring, process chunks in parallel:
490
+
491
+ ```typescript
492
+ execute: async ({ query, chunks }) => {
493
+ const scored = await Promise.all(
494
+ chunks.map(async (chunk) => ({
495
+ chunk,
496
+ score: await computeScore(query, chunk)
497
+ }))
498
+ );
499
+
500
+ scored.sort((a, b) => b.score - a.score);
501
+ return scored.map(s => s.chunk);
502
+ }
503
+ ```
504
+ </Accordion>
505
+ </AccordionGroup>
506
+
507
+ ## Next steps
508
+
509
+ <CardGroup cols={2}>
510
+ <Card title="Configuration" icon="gear" href="/core/exulu-reranker/configuration">
511
+ Learn about configuration options
512
+ </Card>
513
+ <Card title="API reference" icon="code" href="/core/exulu-reranker/api-reference">
514
+ Explore methods and properties
515
+ </Card>
516
+ </CardGroup>