Backend — live runner - New `POST /api/documents/:id/rag` endpoint. Loads `document_json` from SQLite, reconstructs the DoclingDocument, wraps the model id in `ModelIdentifier(ollama_name=...)`, and calls `agent._rag_loop` off-thread (blocking sync call). Returns a `RAGResult` in the shape the existing v1 import path already consumes, so the frontend overlay is fully reused. - `_rag_loop` is private upstream; we call it because `run()` wraps the answer in a synthetic DoclingDocument and drops the iteration trace. - Settings: `RAG_ENABLED`, `OLLAMA_HOST`, `RAG_MODEL_ID`. Router mounts unconditionally; handler 503s when the flag is off or deps aren't installed. `rag_available` surfaced in `/api/health`. - Maps known docling-agent bugs to readable HTTP errors: 502 with "the model couldn't produce a parseable answer" when `_rag_loop` raises `IndexError` from `find_json_dicts([])[0]` after 3 + 3 rejection-sampling retries (model-dependent). - Tests: 11 cases (flag off, query empty, no analysis, happy path, model_id wrap, Ollama env, IndexError → 502, other errors → 500, deps missing → 503). Backend — bug fix - Default `BATCH_PAGE_SIZE` flipped from `10` to `0` to match the dataclass default. The old default silently dropped `document_json` (see `domain/services.merge_results`) for any doc > 10 pages, which broke the reasoning tunnel. Set `BATCH_PAGE_SIZE>0` explicitly on memory-constrained deploys if batching is wanted. Frontend — runner UX - `features/reasoning/api.ts:runReasoning()` — POST wrapper. - `RunReasoningDialog.vue` — query textarea + optional model_id override. Blocks close while running, 20-40s loading state, synthesises a sidecar-shaped envelope so the panel surfaces query + model the same way an imported trace would. - `ReasoningWorkspace.vue` — primary "Run reasoning" button; "Import trace" relegated to ghost secondary. - Store: `runDialogOpen`, `running`, `setRunning`. Frontend — answer polish - Answer rendered through `marked` + DOMPurify (models emit markdown lists; `pre-wrap` rendered them as plain "1. …" strings). - Dedicated answer block with orange border, "ANSWER" label, "Copy" button (clipboard + "Copied ✓" feedback). - IterationCard: drop the duplicate `response` block (the main answer is authoritative); style reasons equal to `"fallback"` (docling-agent `select_from_failure` placeholder) as italic muted "— no structured rationale". Frontend — node details contents - Clicking a SectionHeader (or any node with compound children) lists its contained elements in `NodeDetailsPanel` under a new "Contents" block. Children come from the same `parentMap` used for Cytoscape compound parenting (explicit PARENT_OF + synthetic section scope), inverted once and cached as a computed. - Click a child row → pan the viewport to it + swap the selection. Housekeeping - `cytoscape-navigator` removed from `package-lock.json` (follow-up from the minimap removal in the previous commit).
41 lines
1.7 KiB
TypeScript
41 lines
1.7 KiB
TypeScript
import { apiFetch } from '../../shared/api/http'
|
||
import type { GraphPayload } from '../analysis/graphApi'
|
||
import type { RAGResult } from './types'
|
||
|
||
/**
|
||
* Fetch the reasoning-trace graph for a document — built on the backend from
|
||
* the SQLite `document_json` blob, not Neo4j. This is intentionally decoupled
|
||
* from Maintain's richer Neo4j graph: reasoning only needs the structural
|
||
* view (sections, parent/child, reading order, pages) to overlay iterations
|
||
* onto, and should work even if Neo4j isn't configured.
|
||
*
|
||
* 404 if no completed analysis with `document_json` exists for the doc.
|
||
*/
|
||
export function fetchReasoningGraph(docId: string): Promise<GraphPayload> {
|
||
return apiFetch<GraphPayload>(`/api/documents/${encodeURIComponent(docId)}/reasoning-graph`)
|
||
}
|
||
|
||
/**
|
||
* Kick off a `docling-agent` RAG run against a document and wait for the
|
||
* `RAGResult` (no streaming yet — the backend blocks on `_rag_loop` and
|
||
* returns once the loop converges or hits `max_iterations`).
|
||
*
|
||
* Runs typically take 20–40s depending on the model + Ollama latency. The
|
||
* caller should show a loading state.
|
||
*
|
||
* Errors:
|
||
* - 503 if `RAG_ENABLED=false` server-side or docling-agent isn't installed
|
||
* - 404 if no completed analysis exists for the doc
|
||
* - 500 if the loop itself raises (Ollama unreachable, model missing, …)
|
||
*/
|
||
export function runReasoning(docId: string, query: string, modelId?: string): Promise<RAGResult> {
|
||
return apiFetch<RAGResult>(`/api/documents/${encodeURIComponent(docId)}/rag`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
query,
|
||
// Backend accepts snake_case; don't camelCase here.
|
||
model_id: modelId || undefined,
|
||
}),
|
||
})
|
||
}
|