trustgraph/ts/packages/flow/src/retrieval/graph-rag-service.ts
elpresidank c545213224 feat: add query/retrieval FlowProcessor services and missing runner scripts
Wire up the query and retrieval side of the pipeline so the agent can
answer questions from stored knowledge:

- Triples query service (FalkorDB) — all SPO pattern queries via NATS
- Graph embeddings query service (Qdrant) — entity vector similarity
- Document embeddings query service (Qdrant) — chunk vector similarity
- Graph RAG service — full concept→entity→traverse→score→synthesize pipeline
- Document RAG service — embed→find chunks→synthesize pipeline
- Runner scripts for chunker, extractor, embeddings (missing from Phase 5)
- Add DocumentEmbeddingsRequest/Response schema types
- Add RAG prompt templates (extract-concepts, edge-scoring, synthesize)
- Add graph/doc embeddings query topics to seed config + flow manager
- Add all pipeline/query/retrieval services to docker-compose
- 8 new runner scripts, 8 new pnpm script aliases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 01:05:54 -05:00

133 lines
4.1 KiB
TypeScript

/**
* Graph RAG service — FlowProcessor wrapper around the GraphRag class.
*
* Consumes GraphRagRequest messages from the agent/gateway, runs the full
* Graph RAG pipeline (concept extraction → entity lookup → graph traversal →
* edge scoring → answer synthesis), and emits GraphRagResponse.
*
* Each request gets its own GraphRag instance to prevent data leakage
* across requests (security requirement from the Python implementation).
*
* Python reference: trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py
*/
import {
FlowProcessor,
ConsumerSpec,
ProducerSpec,
RequestResponseSpec,
type ProcessorConfig,
type FlowContext,
type GraphRagRequest,
type GraphRagResponse,
type TextCompletionRequest,
type TextCompletionResponse,
type EmbeddingsRequest,
type EmbeddingsResponse,
type GraphEmbeddingsRequest,
type GraphEmbeddingsResponse,
type TriplesQueryRequest,
type TriplesQueryResponse,
type PromptRequest,
type PromptResponse,
} from "@trustgraph/base";
import { GraphRag } from "./graph-rag.js";
export class GraphRagService extends FlowProcessor {
constructor(config: ProcessorConfig) {
super(config);
// Consumer: graph RAG requests
this.registerSpecification(
new ConsumerSpec<GraphRagRequest>("graph-rag-request", this.onRequest.bind(this)),
);
// Producer: graph RAG responses
this.registerSpecification(new ProducerSpec<GraphRagResponse>("graph-rag-response"));
// Request-response clients for the pipeline
this.registerSpecification(
new RequestResponseSpec<TextCompletionRequest, TextCompletionResponse>(
"llm",
"text-completion-request",
"text-completion-response",
),
);
this.registerSpecification(
new RequestResponseSpec<EmbeddingsRequest, EmbeddingsResponse>(
"embeddings",
"embeddings-request",
"embeddings-response",
),
);
this.registerSpecification(
new RequestResponseSpec<GraphEmbeddingsRequest, GraphEmbeddingsResponse>(
"graph-embeddings",
"graph-embeddings-request",
"graph-embeddings-response",
),
);
this.registerSpecification(
new RequestResponseSpec<TriplesQueryRequest, TriplesQueryResponse>(
"triples",
"triples-request",
"triples-response",
),
);
this.registerSpecification(
new RequestResponseSpec<PromptRequest, PromptResponse>(
"prompt",
"prompt-request",
"prompt-response",
),
);
console.log("[GraphRag] Service initialized");
}
private async onRequest(
msg: GraphRagRequest,
properties: Record<string, string>,
flowCtx: FlowContext,
): Promise<void> {
const requestId = properties.id;
if (!requestId) return;
const producer = flowCtx.flow.producer<GraphRagResponse>("graph-rag-response");
try {
// Create a per-request GraphRag instance with flow clients
const graphRag = new GraphRag(
{
llm: flowCtx.flow.requestor<TextCompletionRequest, TextCompletionResponse>("llm"),
embeddings: flowCtx.flow.requestor<EmbeddingsRequest, EmbeddingsResponse>("embeddings"),
graphEmbeddings: flowCtx.flow.requestor<GraphEmbeddingsRequest, GraphEmbeddingsResponse>("graph-embeddings"),
triples: flowCtx.flow.requestor<TriplesQueryRequest, TriplesQueryResponse>("triples"),
prompt: flowCtx.flow.requestor<PromptRequest, PromptResponse>("prompt"),
},
{
entityLimit: msg.entityLimit,
tripleLimit: msg.tripleLimit,
maxSubgraphSize: msg.maxSubgraphSize,
maxPathLength: msg.maxPathLength,
},
);
const response = await graphRag.query(msg.query, {
collection: msg.collection,
});
await producer.send(requestId, { response });
} catch (err) {
console.error("[GraphRag] Query failed:", err);
await producer.send(requestId, {
response: "",
error: { type: "rag-error", message: String(err) },
});
}
}
}
export async function run(): Promise<void> {
await GraphRagService.launch("graph-rag");
}