2026-04-05 21:09:33 -05:00
|
|
|
/**
|
|
|
|
|
* Document RAG retrieval pipeline.
|
|
|
|
|
*
|
|
|
|
|
* Python reference: trustgraph-flow/trustgraph/retrieval/document_rag/
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import type {
|
feat: fix RAG pipelines, Beep Graph branding, PWA, and ambient glow UI
Pipeline fixes:
- Fix agent getting empty response from graph-rag by combining answer +
explain data in single message (RequestResponse returns first msg)
- Fix Doc RAG pipeline: add content field to Qdrant doc payload, seed 10
document chunks, fix type mismatches across base/flow/client
- Forward explainability events from agent's KnowledgeQuery to client
- Add "agent" to TERM_BEARING_RESPONSE_SERVICES for triple translation
- Fix embeddings env var (OLLAMA_URL), user/collection threading, edge
scoring threshold, and various protocol mismatches
Branding:
- Rename TrustGraph → Beep Graph (title, sidebar, settings, about)
- Custom lambda + ThugLife pixel glasses SVG logo component
- Forest green color palette (brand-50 through brand-900)
- SVG favicon + PNG icons (16/32/180/192/512)
- PWA manifest with service worker for offline shell caching
- Splash screen with animated logo pulse on app load
- Ambient glow background with drifting green radial blobs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:19:10 -05:00
|
|
|
DocumentEmbeddingsRequest,
|
|
|
|
|
DocumentEmbeddingsResponse,
|
2026-06-01 16:22:25 -05:00
|
|
|
EmbeddingsRequest,
|
|
|
|
|
EmbeddingsResponse,
|
|
|
|
|
FlowRequestor,
|
2026-04-05 21:09:33 -05:00
|
|
|
PromptRequest,
|
|
|
|
|
PromptResponse,
|
2026-06-01 16:22:25 -05:00
|
|
|
TextCompletionRequest,
|
|
|
|
|
TextCompletionResponse,
|
2026-04-05 21:09:33 -05:00
|
|
|
} from "@trustgraph/base";
|
2026-06-01 16:22:25 -05:00
|
|
|
import { errorMessage } from "@trustgraph/base";
|
|
|
|
|
import { Context, Effect, Layer } from "effect";
|
|
|
|
|
import * as S from "effect/Schema";
|
2026-04-05 21:09:33 -05:00
|
|
|
|
|
|
|
|
export interface DocumentRagClients {
|
2026-05-12 08:06:58 -05:00
|
|
|
llm: FlowRequestor<TextCompletionRequest, TextCompletionResponse>;
|
|
|
|
|
embeddings: FlowRequestor<EmbeddingsRequest, EmbeddingsResponse>;
|
|
|
|
|
docEmbeddings: FlowRequestor<DocumentEmbeddingsRequest, DocumentEmbeddingsResponse>;
|
|
|
|
|
prompt: FlowRequestor<PromptRequest, PromptResponse>;
|
2026-04-05 21:09:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type ChunkCallback = (text: string, endOfStream: boolean) => Promise<void>;
|
|
|
|
|
|
2026-06-01 16:22:25 -05:00
|
|
|
export interface DocumentRagQueryOptions {
|
|
|
|
|
readonly collection?: string;
|
|
|
|
|
readonly streaming?: boolean;
|
|
|
|
|
readonly chunkCallback?: ChunkCallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class DocumentRagEngineError extends S.TaggedErrorClass<DocumentRagEngineError>()(
|
|
|
|
|
"DocumentRagEngineError",
|
|
|
|
|
{
|
|
|
|
|
message: S.String,
|
|
|
|
|
operation: S.String,
|
|
|
|
|
cause: S.DefectWithStack,
|
|
|
|
|
},
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
export interface DocumentRagEngineShape {
|
|
|
|
|
readonly query: (
|
|
|
|
|
clients: DocumentRagClients,
|
|
|
|
|
queryText: string,
|
|
|
|
|
options?: DocumentRagQueryOptions,
|
|
|
|
|
) => Effect.Effect<string, DocumentRagEngineError>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class DocumentRagEngine extends Context.Service<DocumentRagEngine, DocumentRagEngineShape>()(
|
|
|
|
|
"@trustgraph/flow/retrieval/document-rag/DocumentRagEngine",
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
const documentRagError = (operation: string, cause: unknown) =>
|
|
|
|
|
new DocumentRagEngineError({
|
|
|
|
|
operation,
|
|
|
|
|
cause,
|
|
|
|
|
message: errorMessage(cause),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export function makeDocumentRagEngine(): DocumentRagEngineShape {
|
|
|
|
|
return {
|
|
|
|
|
query: Effect.fn("DocumentRagEngine.query")((
|
|
|
|
|
clients: DocumentRagClients,
|
|
|
|
|
queryText: string,
|
|
|
|
|
options?: DocumentRagQueryOptions,
|
|
|
|
|
) =>
|
|
|
|
|
Effect.tryPromise({
|
|
|
|
|
try: () => queryDocumentRag(clients, queryText, options),
|
|
|
|
|
catch: (cause) => documentRagError("query", cause),
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const DocumentRagLive: Layer.Layer<DocumentRagEngine> = Layer.succeed(
|
|
|
|
|
DocumentRagEngine,
|
|
|
|
|
DocumentRagEngine.of(makeDocumentRagEngine()),
|
|
|
|
|
);
|
|
|
|
|
|
2026-06-01 20:26:47 -05:00
|
|
|
export interface DocumentRag {
|
|
|
|
|
readonly query: (
|
2026-04-05 21:09:33 -05:00
|
|
|
queryText: string,
|
2026-06-01 16:22:25 -05:00
|
|
|
options?: DocumentRagQueryOptions,
|
2026-06-01 20:26:47 -05:00
|
|
|
) => Promise<string>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function makeDocumentRag(clients: DocumentRagClients): DocumentRag {
|
|
|
|
|
const engine = makeDocumentRagEngine();
|
|
|
|
|
return {
|
|
|
|
|
query: (queryText, options) =>
|
|
|
|
|
Effect.runPromise(engine.query(clients, queryText, options)),
|
|
|
|
|
};
|
2026-04-05 21:09:33 -05:00
|
|
|
}
|
2026-06-01 16:22:25 -05:00
|
|
|
|
|
|
|
|
async function queryDocumentRag(
|
|
|
|
|
clients: DocumentRagClients,
|
|
|
|
|
queryText: string,
|
|
|
|
|
options?: DocumentRagQueryOptions,
|
|
|
|
|
): Promise<string> {
|
|
|
|
|
const collection = options?.collection ?? "default";
|
|
|
|
|
|
|
|
|
|
const embResp = await clients.embeddings.request({ text: [queryText] });
|
|
|
|
|
const vectors = embResp.vectors;
|
|
|
|
|
|
|
|
|
|
const docResp = await clients.docEmbeddings.request({
|
|
|
|
|
vectors,
|
|
|
|
|
limit: 10,
|
|
|
|
|
collection,
|
|
|
|
|
user: "default",
|
|
|
|
|
});
|
|
|
|
|
const chunks = docResp.chunks ?? [];
|
|
|
|
|
console.log(`[DocumentRag] Found ${chunks.length} matching chunks`);
|
|
|
|
|
|
|
|
|
|
const context = chunks
|
|
|
|
|
.flatMap((chunk) =>
|
|
|
|
|
chunk.content !== undefined && chunk.content.length > 0 ? [chunk.content] : [],
|
|
|
|
|
)
|
|
|
|
|
.join("\n\n---\n\n");
|
|
|
|
|
|
|
|
|
|
const promptResp = await clients.prompt.request({
|
|
|
|
|
name: "document-rag-synthesize",
|
|
|
|
|
variables: { query: queryText, context },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const resp = await clients.llm.request({
|
|
|
|
|
system: promptResp.system,
|
|
|
|
|
prompt: promptResp.prompt,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return resp.response;
|
|
|
|
|
}
|