trustgraph/ts/packages/flow/src/retrieval/document-rag-service.ts

151 lines
4.5 KiB
TypeScript
Raw Normal View History

/**
2026-06-01 16:22:25 -05:00
* Document RAG service.
*
2026-06-01 16:22:25 -05:00
* Consumes DocumentRagRequest messages, runs the document retrieval pipeline,
* and emits DocumentRagResponse.
*
* Python reference: trustgraph-flow/trustgraph/retrieval/document_rag/
*/
2026-06-02 00:54:47 -05:00
import {NodeRuntime} from "@effect/platform-node";
import {
2026-06-01 20:26:47 -05:00
makeConsumerSpec,
makeFlowProcessor,
makeProducerSpec,
makeRequestResponseSpec,
2026-06-01 16:22:25 -05:00
makeFlowProcessorProgram,
type DocumentEmbeddingsRequest,
type DocumentEmbeddingsResponse,
type DocumentRagRequest,
type DocumentRagResponse,
type EmbeddingsRequest,
type EmbeddingsResponse,
2026-06-01 16:22:25 -05:00
type FlowContext,
2026-06-01 20:26:47 -05:00
type FlowProcessorRuntime,
2026-06-01 16:22:25 -05:00
type FlowResourceNotFoundError,
type MessagingDeliveryError,
type ProcessorConfig,
type PromptRequest,
type PromptResponse,
2026-06-01 16:22:25 -05:00
type Spec,
type TextCompletionRequest,
type TextCompletionResponse,
} from "@trustgraph/base";
2026-06-02 00:54:47 -05:00
import {Effect, Layer, ManagedRuntime} from "effect";
2026-06-01 16:22:25 -05:00
import {
DocumentRagEngine,
DocumentRagEngineError,
DocumentRagLive,
makeDocumentRagEngine,
type DocumentRagClients,
} from "./document-rag.js";
2026-06-02 03:23:23 -05:00
const DocumentRagResponseProducer = makeProducerSpec<DocumentRagResponse>("document-rag-response");
const DocumentRagLlmClient = makeRequestResponseSpec<TextCompletionRequest, TextCompletionResponse>(
"llm",
"text-completion-request",
"text-completion-response",
);
const DocumentRagEmbeddingsClient = makeRequestResponseSpec<EmbeddingsRequest, EmbeddingsResponse>(
"embeddings",
"embeddings-request",
"embeddings-response",
);
const DocumentRagDocEmbeddingsClient = makeRequestResponseSpec<DocumentEmbeddingsRequest, DocumentEmbeddingsResponse>(
"doc-embeddings",
"document-embeddings-request",
"document-embeddings-response",
);
const DocumentRagPromptClient = makeRequestResponseSpec<PromptRequest, PromptResponse>(
"prompt",
"prompt-request",
"prompt-response",
);
2026-06-01 16:22:25 -05:00
const onDocumentRagRequest = Effect.fn("DocumentRagService.onRequest")(function* (
msg: DocumentRagRequest,
properties: Record<string, string>,
flowCtx: FlowContext<DocumentRagEngine>,
) {
const requestId = properties.id;
if (requestId === undefined || requestId.length === 0) return;
2026-06-02 03:23:23 -05:00
const producer = yield* flowCtx.flow.producerEffect(DocumentRagResponseProducer);
2026-06-01 16:22:25 -05:00
const engine = yield* DocumentRagEngine;
2026-06-01 16:22:25 -05:00
const clients: DocumentRagClients = {
2026-06-02 03:23:23 -05:00
llm: yield* flowCtx.flow.requestorEffect(DocumentRagLlmClient),
embeddings: yield* flowCtx.flow.requestorEffect(DocumentRagEmbeddingsClient),
docEmbeddings: yield* flowCtx.flow.requestorEffect(DocumentRagDocEmbeddingsClient),
prompt: yield* flowCtx.flow.requestorEffect(DocumentRagPromptClient),
2026-06-01 16:22:25 -05:00
};
2026-06-01 16:22:25 -05:00
const response = yield* engine.query(
clients,
msg.query,
{
...(msg.collection !== undefined ? { collection: msg.collection } : {}),
},
).pipe(
Effect.catch((error: DocumentRagEngineError) =>
Effect.logError("[DocumentRag] Query failed", {
error: error.message,
operation: error.operation,
}).pipe(
Effect.flatMap(() =>
producer.send(requestId, {
response: "",
error: { type: "rag-error", message: error.message },
}),
),
Effect.as(undefined),
),
),
);
2026-06-01 16:22:25 -05:00
if (response === undefined) return;
yield* producer.send(requestId, { response, endOfStream: true });
});
2026-06-01 16:22:25 -05:00
export const makeDocumentRagSpecs = (): ReadonlyArray<Spec<DocumentRagEngine>> => [
2026-06-01 20:26:47 -05:00
makeConsumerSpec<DocumentRagRequest, FlowResourceNotFoundError | MessagingDeliveryError, DocumentRagEngine>(
2026-06-01 16:22:25 -05:00
"document-rag-request",
onDocumentRagRequest,
),
2026-06-02 03:23:23 -05:00
DocumentRagResponseProducer,
DocumentRagLlmClient,
DocumentRagEmbeddingsClient,
DocumentRagDocEmbeddingsClient,
DocumentRagPromptClient,
2026-06-01 16:22:25 -05:00
];
2026-06-01 20:26:47 -05:00
export type DocumentRagService = FlowProcessorRuntime<DocumentRagEngine>;
2026-06-01 16:22:25 -05:00
2026-06-01 20:26:47 -05:00
export function makeDocumentRagService(config: ProcessorConfig): DocumentRagService {
return makeFlowProcessor(config, {
specifications: makeDocumentRagSpecs(),
provide: (effect) =>
effect.pipe(
Effect.provideService(DocumentRagEngine, DocumentRagEngine.of(makeDocumentRagEngine())),
),
});
}
2026-06-01 20:26:47 -05:00
export const DocumentRagService = makeDocumentRagService;
2026-06-01 16:22:25 -05:00
export const program = makeFlowProcessorProgram({
2026-05-12 08:06:58 -05:00
id: "document-rag",
2026-06-01 16:22:25 -05:00
specs: makeDocumentRagSpecs,
layer: () => DocumentRagLive,
2026-05-12 08:06:58 -05:00
});
2026-06-02 00:54:47 -05:00
const documentRagRuntime = ManagedRuntime.make(Layer.empty);
2026-06-01 23:19:54 -05:00
export function run(): Promise<void> {
2026-06-02 00:54:47 -05:00
return documentRagRuntime.runPromise(program);
}
export function runMain(): void {
NodeRuntime.runMain(program);
}