mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 09:29:38 +02:00
saving
This commit is contained in:
parent
e8c7a4f6e0
commit
ffd97375a8
160 changed files with 6704 additions and 1895 deletions
|
|
@ -27,6 +27,7 @@ import {
|
|||
type PromptRequest,
|
||||
type PromptResponse,
|
||||
} from "@trustgraph/base";
|
||||
import { makeProcessorProgram } from "@trustgraph/base";
|
||||
import { DocumentRag } from "./document-rag.js";
|
||||
|
||||
export class DocumentRagService extends FlowProcessor {
|
||||
|
|
@ -35,7 +36,7 @@ export class DocumentRagService extends FlowProcessor {
|
|||
|
||||
// Consumer: document RAG requests
|
||||
this.registerSpecification(
|
||||
new ConsumerSpec<DocumentRagRequest>("document-rag-request", this.onRequest.bind(this)),
|
||||
ConsumerSpec.fromPromise<DocumentRagRequest>("document-rag-request", this.onRequest.bind(this)),
|
||||
);
|
||||
|
||||
// Producer: document RAG responses
|
||||
|
|
@ -80,7 +81,7 @@ export class DocumentRagService extends FlowProcessor {
|
|||
flowCtx: FlowContext,
|
||||
): Promise<void> {
|
||||
const requestId = properties.id;
|
||||
if (!requestId) return;
|
||||
if (requestId === undefined || requestId.length === 0) return;
|
||||
|
||||
const producer = flowCtx.flow.producer<DocumentRagResponse>("document-rag-response");
|
||||
|
||||
|
|
@ -93,7 +94,7 @@ export class DocumentRagService extends FlowProcessor {
|
|||
});
|
||||
|
||||
const response = await documentRag.query(msg.query, {
|
||||
collection: msg.collection,
|
||||
...(msg.collection !== undefined ? { collection: msg.collection } : {}),
|
||||
});
|
||||
|
||||
await producer.send(requestId, { response, endOfStream: true });
|
||||
|
|
@ -107,6 +108,11 @@ export class DocumentRagService extends FlowProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
export const program = makeProcessorProgram({
|
||||
id: "document-rag",
|
||||
make: (config) => new DocumentRagService(config),
|
||||
});
|
||||
|
||||
export async function run(): Promise<void> {
|
||||
await DocumentRagService.launch("document-rag");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
import type {
|
||||
RequestResponse,
|
||||
FlowRequestor,
|
||||
TextCompletionRequest,
|
||||
TextCompletionResponse,
|
||||
EmbeddingsRequest,
|
||||
|
|
@ -20,16 +20,20 @@ import type {
|
|||
} from "@trustgraph/base";
|
||||
|
||||
export interface DocumentRagClients {
|
||||
llm: RequestResponse<TextCompletionRequest, TextCompletionResponse>;
|
||||
embeddings: RequestResponse<EmbeddingsRequest, EmbeddingsResponse>;
|
||||
docEmbeddings: RequestResponse<DocumentEmbeddingsRequest, DocumentEmbeddingsResponse>;
|
||||
prompt: RequestResponse<PromptRequest, PromptResponse>;
|
||||
llm: FlowRequestor<TextCompletionRequest, TextCompletionResponse>;
|
||||
embeddings: FlowRequestor<EmbeddingsRequest, EmbeddingsResponse>;
|
||||
docEmbeddings: FlowRequestor<DocumentEmbeddingsRequest, DocumentEmbeddingsResponse>;
|
||||
prompt: FlowRequestor<PromptRequest, PromptResponse>;
|
||||
}
|
||||
|
||||
export type ChunkCallback = (text: string, endOfStream: boolean) => Promise<void>;
|
||||
|
||||
export class DocumentRag {
|
||||
constructor(private readonly clients: DocumentRagClients) {}
|
||||
private readonly clients: DocumentRagClients;
|
||||
|
||||
constructor(clients: DocumentRagClients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
async query(
|
||||
queryText: string,
|
||||
|
|
@ -57,8 +61,9 @@ export class DocumentRag {
|
|||
|
||||
// Step 3: Build context from chunks
|
||||
const context = chunks
|
||||
.filter((c) => c.content)
|
||||
.map((c) => c.content)
|
||||
.flatMap((c) =>
|
||||
c.content !== undefined && c.content.length > 0 ? [c.content] : [],
|
||||
)
|
||||
.join("\n\n---\n\n");
|
||||
|
||||
// Step 4: Synthesize answer
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import {
|
|||
type PromptRequest,
|
||||
type PromptResponse,
|
||||
} from "@trustgraph/base";
|
||||
import { makeProcessorProgram } from "@trustgraph/base";
|
||||
import { GraphRag } from "./graph-rag.js";
|
||||
|
||||
export class GraphRagService extends FlowProcessor {
|
||||
|
|
@ -39,7 +40,7 @@ export class GraphRagService extends FlowProcessor {
|
|||
|
||||
// Consumer: graph RAG requests
|
||||
this.registerSpecification(
|
||||
new ConsumerSpec<GraphRagRequest>("graph-rag-request", this.onRequest.bind(this)),
|
||||
ConsumerSpec.fromPromise<GraphRagRequest>("graph-rag-request", this.onRequest.bind(this)),
|
||||
);
|
||||
|
||||
// Producer: graph RAG responses
|
||||
|
|
@ -91,7 +92,7 @@ export class GraphRagService extends FlowProcessor {
|
|||
flowCtx: FlowContext,
|
||||
): Promise<void> {
|
||||
const requestId = properties.id;
|
||||
if (!requestId) return;
|
||||
if (requestId === undefined || requestId.length === 0) return;
|
||||
|
||||
const producer = flowCtx.flow.producer<GraphRagResponse>("graph-rag-response");
|
||||
console.log(`[GraphRagService] Received request ${requestId}: "${msg.query?.slice(0, 60)}..." collection=${msg.collection}`);
|
||||
|
|
@ -107,15 +108,17 @@ export class GraphRagService extends FlowProcessor {
|
|||
prompt: flowCtx.flow.requestor<PromptRequest, PromptResponse>("prompt"),
|
||||
},
|
||||
{
|
||||
entityLimit: msg.entityLimit,
|
||||
tripleLimit: msg.tripleLimit,
|
||||
maxSubgraphSize: msg.maxSubgraphSize,
|
||||
maxPathLength: msg.maxPathLength,
|
||||
...(msg.entityLimit !== undefined ? { entityLimit: msg.entityLimit } : {}),
|
||||
...(msg.tripleLimit !== undefined ? { tripleLimit: msg.tripleLimit } : {}),
|
||||
...(msg.maxSubgraphSize !== undefined
|
||||
? { maxSubgraphSize: msg.maxSubgraphSize }
|
||||
: {}),
|
||||
...(msg.maxPathLength !== undefined ? { maxPathLength: msg.maxPathLength } : {}),
|
||||
},
|
||||
);
|
||||
|
||||
const result = await graphRag.query(msg.query, {
|
||||
collection: msg.collection,
|
||||
...(msg.collection !== undefined ? { collection: msg.collection } : {}),
|
||||
});
|
||||
|
||||
// Send answer with explain data embedded in a SINGLE message.
|
||||
|
|
@ -145,6 +148,11 @@ export class GraphRagService extends FlowProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
export const program = makeProcessorProgram({
|
||||
id: "graph-rag",
|
||||
make: (config) => new GraphRagService(config),
|
||||
});
|
||||
|
||||
export async function run(): Promise<void> {
|
||||
await GraphRagService.launch("graph-rag");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ import type {
|
|||
EmbeddingsResponse,
|
||||
GraphEmbeddingsRequest,
|
||||
GraphEmbeddingsResponse,
|
||||
FlowRequestor,
|
||||
PromptRequest,
|
||||
PromptResponse,
|
||||
RequestResponse,
|
||||
Term,
|
||||
TextCompletionRequest,
|
||||
TextCompletionResponse,
|
||||
|
|
@ -37,11 +37,11 @@ export interface GraphRagConfig {
|
|||
}
|
||||
|
||||
export interface GraphRagClients {
|
||||
llm: RequestResponse<TextCompletionRequest, TextCompletionResponse>;
|
||||
embeddings: RequestResponse<EmbeddingsRequest, EmbeddingsResponse>;
|
||||
graphEmbeddings: RequestResponse<GraphEmbeddingsRequest, GraphEmbeddingsResponse>;
|
||||
triples: RequestResponse<TriplesQueryRequest, TriplesQueryResponse>;
|
||||
prompt: RequestResponse<PromptRequest, PromptResponse>;
|
||||
llm: FlowRequestor<TextCompletionRequest, TextCompletionResponse>;
|
||||
embeddings: FlowRequestor<EmbeddingsRequest, EmbeddingsResponse>;
|
||||
graphEmbeddings: FlowRequestor<GraphEmbeddingsRequest, GraphEmbeddingsResponse>;
|
||||
triples: FlowRequestor<TriplesQueryRequest, TriplesQueryResponse>;
|
||||
prompt: FlowRequestor<PromptRequest, PromptResponse>;
|
||||
}
|
||||
|
||||
export type ChunkCallback = (text: string, endOfStream: boolean) => Promise<void>;
|
||||
|
|
@ -52,12 +52,14 @@ export interface GraphRagResult {
|
|||
}
|
||||
|
||||
export class GraphRag {
|
||||
private readonly clients: GraphRagClients;
|
||||
private config: Required<GraphRagConfig>;
|
||||
|
||||
constructor(
|
||||
private readonly clients: GraphRagClients,
|
||||
clients: GraphRagClients,
|
||||
config: GraphRagConfig = {},
|
||||
) {
|
||||
this.clients = clients;
|
||||
this.config = {
|
||||
entityLimit: config.entityLimit ?? 50,
|
||||
tripleLimit: config.tripleLimit ?? 30,
|
||||
|
|
@ -125,7 +127,7 @@ export class GraphRag {
|
|||
return (llmResp as TextCompletionResponse).response
|
||||
.split("\n")
|
||||
.map((c) => c.trim())
|
||||
.filter(Boolean);
|
||||
.filter((c) => c.length > 0);
|
||||
}
|
||||
|
||||
private async getVectors(concepts: string[]): Promise<number[][]> {
|
||||
|
|
@ -166,11 +168,12 @@ export class GraphRag {
|
|||
// Query each entity as subject to get outgoing edges
|
||||
const queries = unvisited.map((entityStr) => {
|
||||
const term = stringToTerm(entityStr);
|
||||
return this.clients.triples.request({
|
||||
const request: TriplesQueryRequest = {
|
||||
s: term,
|
||||
collection,
|
||||
limit: this.config.tripleLimit,
|
||||
});
|
||||
...(collection !== undefined ? { collection } : {}),
|
||||
};
|
||||
return this.clients.triples.request(request);
|
||||
});
|
||||
|
||||
const results = await Promise.all(queries);
|
||||
|
|
@ -257,7 +260,12 @@ export class GraphRag {
|
|||
const parsed = JSON.parse(responseText) as Array<{ id: string; score: number }>;
|
||||
if (Array.isArray(parsed)) {
|
||||
for (const item of parsed) {
|
||||
if (item && typeof item.id === "string" && typeof item.score === "number") {
|
||||
if (
|
||||
typeof item === "object" &&
|
||||
item !== null &&
|
||||
typeof item.id === "string" &&
|
||||
typeof item.score === "number"
|
||||
) {
|
||||
scored.push({ id: item.id, score: item.score });
|
||||
}
|
||||
}
|
||||
|
|
@ -266,10 +274,15 @@ export class GraphRag {
|
|||
// Fall back to parsing line-by-line JSON objects
|
||||
for (const line of responseText.split("\n")) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
if (trimmed.length === 0) continue;
|
||||
try {
|
||||
const obj = JSON.parse(trimmed) as { id?: string; score?: number };
|
||||
if (obj && typeof obj.id === "string" && typeof obj.score === "number") {
|
||||
if (
|
||||
typeof obj === "object" &&
|
||||
obj !== null &&
|
||||
typeof obj.id === "string" &&
|
||||
typeof obj.score === "number"
|
||||
) {
|
||||
scored.push({ id: obj.id, score: obj.score });
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -281,8 +294,6 @@ export class GraphRag {
|
|||
// Sort by score descending and keep top N
|
||||
scored.sort((a, b) => b.score - a.score);
|
||||
const topN = scored.slice(0, this.config.edgeLimit);
|
||||
const selectedIds = new Set(topN.map((e) => e.id));
|
||||
|
||||
// Map back to triples
|
||||
const result: Triple[] = [];
|
||||
for (const entry of topN) {
|
||||
|
|
@ -317,7 +328,7 @@ export class GraphRag {
|
|||
variables: { query, context },
|
||||
});
|
||||
|
||||
if (chunkCallback) {
|
||||
if (chunkCallback !== undefined) {
|
||||
// Streaming response
|
||||
let fullText = "";
|
||||
await this.clients.llm.request(
|
||||
|
|
@ -329,11 +340,11 @@ export class GraphRag {
|
|||
{
|
||||
recipient: async (resp) => {
|
||||
const r = resp as TextCompletionResponse;
|
||||
if (r.response) {
|
||||
if (r.response.length > 0) {
|
||||
fullText += r.response;
|
||||
await chunkCallback(r.response, !!r.endOfStream);
|
||||
await chunkCallback(r.response, r.endOfStream === true);
|
||||
}
|
||||
return !!r.endOfStream;
|
||||
return r.endOfStream === true;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue