UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

83 lines • 3.26 kB
import { errorMessageOf, HttpModelError } from './openai-embeddings.js'; /** * A cross-encoder {@link Reranker} behind an HTTP `/rerank` endpoint — the Cohere-style contract * most providers and self-hosted servers share. Compose it with `RerankingRetriever`: * * ```ts * const reranker = new HttpReranker({ url: 'https://api.cohere.com/v2/rerank', model: 'rerank-v3.5', apiKey }); * const retriever = new RerankingRetriever(hybrid, reranker, { fetchTopK: 30 }); * ``` * * Reads `results: [{ index, relevance_score }]` (Cohere, Jina), `data: [{ index, relevance_score }]` * (Voyage) and a bare `[{ index, score }]` (TEI). Each passage keeps its fields and takes the new * score; the list is re-sorted and cut to `topK`. Indices the server invents are ignored. */ export class HttpReranker { options; format; doFetch; constructor(options) { this.options = options; this.format = options.format ?? 'auto'; this.doFetch = options.fetch ?? fetch; } async rerank(query, passages, options = {}) { if (passages.length === 0) { return []; } const topK = options.topK ?? passages.length; const documents = passages.map((passage) => passage.text); const response = await this.doFetch(this.options.url, { method: 'POST', headers: { 'content-type': 'application/json', ...(this.options.apiKey ? { authorization: `Bearer ${this.options.apiKey}` } : {}), ...this.options.headers, }, body: JSON.stringify({ ...(this.options.model !== undefined ? { model: this.options.model } : {}), query, ...(this.format !== 'tei' ? { documents, top_n: topK } : {}), ...(this.format !== 'cohere' ? { texts: documents } : {}), }), signal: AbortSignal.timeout(this.options.timeoutMs ?? 30_000), }); const body = await response.text(); if (!response.ok) { throw new HttpModelError(response.status, `Rerank request failed (${response.status}): ${errorMessageOf(body)}`); } const items = itemsOf(body); if (items === undefined) { throw new HttpModelError(502, 'Rerank response has no results'); } const seen = new Set(); const reranked = []; for (const item of items) { const index = item.index; const passage = typeof index === 'number' ? passages[index] : undefined; if (passage === undefined || seen.has(index)) { continue; } seen.add(index); reranked.push({ ...passage, score: Number(item.relevance_score ?? item.score ?? 0) }); } reranked.sort((a, b) => b.score - a.score); return reranked.slice(0, topK); } } function itemsOf(body) { let json; try { json = JSON.parse(body); } catch { return undefined; } if (Array.isArray(json)) { return json; } const wrapped = json; const items = wrapped?.results ?? wrapped?.data; return Array.isArray(items) ? items : undefined; } //# sourceMappingURL=http-reranker.js.map