import { describe, expect, it } from "@effect/vitest"; import { Effect } from "effect"; import type { DocumentEmbeddingsRequest, DocumentEmbeddingsResponse, EmbeddingsRequest, EmbeddingsResponse, FlowRequestor, GraphEmbeddingsRequest, GraphEmbeddingsResponse, PromptRequest, PromptResponse, TextCompletionRequest, TextCompletionResponse, TriplesQueryRequest, TriplesQueryResponse, } from "@trustgraph/base"; import { makeDocumentRagEngine, type DocumentRagClients } from "../retrieval/document-rag.js"; import { makeGraphRagEngine, type GraphRagClients } from "../retrieval/graph-rag.js"; const requestor = ( handler: (request: TReq) => TRes | Promise, ): FlowRequestor => ({ request: async (request) => handler(request), stop: async () => undefined, }); describe("RAG engines", () => { it.effect( "runs Graph RAG without per-request service objects", Effect.fnUntraced(function* () { const prompts: Array = []; const triplesRequests: Array = []; let synthesisContext = ""; const clients: GraphRagClients = { prompt: requestor((request) => { prompts.push(request); if (request.name === "extract-concepts") { return { system: "extract-system", prompt: "extract-prompt" }; } synthesisContext = String(request.variables?.context ?? ""); return { system: "synth-system", prompt: "synth-prompt" }; }), llm: requestor((request) => { if (request.prompt === "extract-prompt") { return { response: "alpha\nbeta", endOfStream: true }; } return { response: `answer:${request.prompt}`, endOfStream: true }; }), embeddings: requestor((request) => { expect(request.text).toEqual(["alpha", "beta"]); return { vectors: [[1], [2]] }; }), graphEmbeddings: requestor((request) => { expect(request.collection).toBe("project"); return { entities: [{ type: "IRI", iri: "https://example.test/entity/a" }], }; }), triples: requestor((request) => { triplesRequests.push(request); return { triples: [ { s: { type: "IRI", iri: "https://example.test/entity/a" }, p: { type: "IRI", iri: "https://example.test/relation" }, o: { type: "LITERAL", value: "related value" }, }, ], }; }), }; const engine = makeGraphRagEngine(); const result = yield* engine.query( clients, "who is related?", { collection: "project" }, { maxPathLength: 1 }, ); expect(result.answer).toBe("answer:synth-prompt"); expect(result.subgraph).toHaveLength(1); expect(prompts.map((prompt) => prompt.name)).toEqual([ "extract-concepts", "graph-rag-synthesize", ]); expect(triplesRequests).toHaveLength(1); expect(synthesisContext).toContain("https://example.test/entity/a"); expect(synthesisContext).toContain("related value"); }), ); it.effect( "builds Document RAG synthesis context from returned chunks", Effect.fnUntraced(function* () { let synthesisContext = ""; const clients: DocumentRagClients = { embeddings: requestor((request) => { expect(request.text).toEqual(["explain docs"]); return { vectors: [[0.1, 0.2]] }; }), docEmbeddings: requestor((request) => { expect(request.collection).toBe("docs"); return { chunks: [ { chunkId: "1", score: 0.9, content: "first chunk" }, { chunkId: "2", score: 0.8, content: "" }, { chunkId: "3", score: 0.7, content: "second chunk" }, ], }; }), prompt: requestor((request) => { synthesisContext = String(request.variables?.context ?? ""); return { system: "doc-system", prompt: "doc-prompt" }; }), llm: requestor((request) => ({ response: `doc-answer:${request.prompt}`, endOfStream: true, })), }; const engine = makeDocumentRagEngine(); const response = yield* engine.query(clients, "explain docs", { collection: "docs" }); expect(response).toBe("doc-answer:doc-prompt"); expect(synthesisContext).toBe("first chunk\n\n---\n\nsecond chunk"); }), ); });