UNPKG

@adonis-agora/agent

Version:

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

95 lines • 4.26 kB
import { defineTool } from '../../ai-tool-ref.js'; import { documentIdOf } from '../vector-store.js'; function isInput(value) { return (typeof value === 'object' && value !== null && typeof value.documentId === 'string' && typeof value.question === 'string'); } const inputSchema = { '~standard': { version: 1, vendor: 'adonis-agora-agent', validate(value) { return isInput(value) ? { value: { documentId: value.documentId, question: value.question } } : { issues: [ { message: 'navigate_document expects { documentId: string, question: string }' }, ], }; }, }, }; /** * The agentic half of two-stage retrieval: `navigate_document(documentId, question)`, a `read`-kind * tool the model calls **after** a search put a long document among its hits — it walks that * document's table of contents to the sections that answer and returns their text, with the path and * reasoning (the citation surface: section titles and page ranges). * * `documentId` may also be a chunk id from a search result (`doc#12`); the chunk suffix is dropped. * A document with no tree answers `found: false` with a note, so the model falls back to the * passages it already has instead of retrying. * * Returns a {@link defineTool} tool: export it from an `app/agent_tools` module, or pass it to * `defineConfig({ tools })`. * * ```ts * export const navigateDocument = createNavigateDocumentTool(navigator, { * filter: (ctx) => ({ tenant: ctx.actor.tenantRef }), * }) * ``` */ export function createNavigateDocumentTool(navigator, options = {}) { return defineTool({ name: options.name ?? 'navigate_document', kind: 'read', description: options.description ?? 'Read inside one long document (regulation, contract, filing, manual) by navigating its table of contents to the sections that answer a question. Use it after a search returned a long document: pass that document id and the question. Returns the matching sections with titles and page ranges for citation.', input: inputSchema, ...(options.roles !== undefined ? { roles: options.roles } : {}), ...(options.ability !== undefined ? { ability: options.ability } : {}), }, async (input, ctx) => { if (!isInput(input)) { throw new Error('navigate_document expects { documentId: string, question: string }'); } const filter = options.filter?.(ctx); const navigateOptions = { ...(filter !== undefined ? { filter } : {}), ...(options.budget !== undefined ? { budget: options.budget } : {}), }; let documentId = input.documentId.trim(); let result = await navigator.navigate(input.question, [documentId], navigateOptions); let navigation = result.documents[0]; if (navigation?.stoppedBy === 'not-found') { const bare = documentIdOf(documentId.replace(/#node:\d+$/, '')); if (bare !== documentId) { documentId = bare; result = await navigator.navigate(input.question, [documentId], navigateOptions); navigation = result.documents[0]; } } if (navigation === undefined || navigation.stoppedBy === 'not-found') { return { documentId, found: false, passages: [], nodes: [], steps: [], note: 'This document has no navigable table of contents (it is short, not indexed, or not accessible). Use the search passages instead.', }; } return { documentId, found: true, passages: result.passages, nodes: navigation.nodes, steps: navigation.steps, ...(navigation.stoppedBy !== undefined ? { stoppedBy: navigation.stoppedBy } : {}), ...(navigation.nodes.length === 0 ? { note: 'No section of this document looked relevant to the question.' } : {}), }; }); } //# sourceMappingURL=navigate-tool.js.map