mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 09:29:38 +02:00
fix: FalkorDB result parsing, embeddings routing, triples query response, graph visualization
- Fix FalkorDB triples query: client v5 returns objects not arrays, use named field access - Fix embeddings service: align spec names to "embeddings-request"/"embeddings-response" - Fix client triplesQuery: read `triples` field instead of `response` from backend - Fix graph page crash: guard against non-array triples, accept literals as entity nodes - Add seed:demo script for AI industry knowledge graph (254 triples, 64 entities) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
580ee319a3
commit
f2b376abef
7 changed files with 837 additions and 31 deletions
|
|
@ -18,11 +18,11 @@ export abstract class EmbeddingsService extends FlowProcessor {
|
|||
|
||||
this.registerSpecification(
|
||||
new ConsumerSpec<EmbeddingsRequest>(
|
||||
"request",
|
||||
"embeddings-request",
|
||||
this.onRequest.bind(this),
|
||||
),
|
||||
);
|
||||
this.registerSpecification(new ProducerSpec<EmbeddingsResponse>("response"));
|
||||
this.registerSpecification(new ProducerSpec<EmbeddingsResponse>("embeddings-response"));
|
||||
this.registerSpecification(new ParameterSpec("model"));
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ export abstract class EmbeddingsService extends FlowProcessor {
|
|||
const requestId = properties.id;
|
||||
if (!requestId) return;
|
||||
|
||||
const responseProducer = flowCtx.flow.producer<EmbeddingsResponse>("response");
|
||||
const responseProducer = flowCtx.flow.producer<EmbeddingsResponse>("embeddings-response");
|
||||
|
||||
try {
|
||||
const vectors = await this.onEmbeddings(msg.text, msg.model);
|
||||
|
|
|
|||
|
|
@ -182,7 +182,9 @@ export interface TriplesQueryRequest {
|
|||
}
|
||||
|
||||
export interface TriplesQueryResponse {
|
||||
response: Triple[];
|
||||
triples: Triple[];
|
||||
/** @deprecated Use `triples` — kept for backward compatibility */
|
||||
response?: Triple[];
|
||||
}
|
||||
|
||||
export interface RowsQueryRequest {
|
||||
|
|
|
|||
|
|
@ -1815,7 +1815,7 @@ export class FlowApi {
|
|||
undefined,
|
||||
this.flowId,
|
||||
)
|
||||
.then((r) => r.response);
|
||||
.then((r) => r.triples ?? r.response ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ function createTerm(value: string): Term {
|
|||
return { type: "LITERAL", value };
|
||||
}
|
||||
|
||||
/** Extract a string field from a FalkorDB result row (returns object with named keys). */
|
||||
function field(row: unknown, key: string): string {
|
||||
return (row as Record<string, unknown>)?.[key] as string ?? "";
|
||||
}
|
||||
|
||||
export class FalkorDBTriplesQuery {
|
||||
private graph: Graph;
|
||||
private connectPromise: Promise<void>;
|
||||
|
|
@ -117,7 +122,7 @@ export class FalkorDBTriplesQuery {
|
|||
`RETURN src.uri LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv, dest: ov } },
|
||||
);
|
||||
for (const _rec of (result.data ?? []) as unknown[][]) {
|
||||
for (const _rec of (result.data ?? [])) {
|
||||
out.push([sv, pv, ov]);
|
||||
}
|
||||
}
|
||||
|
|
@ -133,8 +138,8 @@ export class FalkorDBTriplesQuery {
|
|||
`RETURN dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? []) as string[][]) {
|
||||
out.push([sv, pv, rec[0] as string]);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([sv, pv, field(rec, "dest")]);
|
||||
}
|
||||
// Nodes
|
||||
const nodeResult = await this.graph.query(
|
||||
|
|
@ -142,8 +147,8 @@ export class FalkorDBTriplesQuery {
|
|||
`RETURN dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? []) as string[][]) {
|
||||
out.push([sv, pv, rec[0] as string]);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([sv, pv, field(rec, "dest")]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,8 +162,8 @@ export class FalkorDBTriplesQuery {
|
|||
`RETURN rel.uri as rel LIMIT ${limit}`,
|
||||
{ params: { src: sv, dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? []) as string[][]) {
|
||||
out.push([sv, rec[0] as string, ov]);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), ov]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -173,8 +178,8 @@ export class FalkorDBTriplesQuery {
|
|||
`RETURN src.uri as src LIMIT ${limit}`,
|
||||
{ params: { rel: pv, dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? []) as string[][]) {
|
||||
out.push([rec[0] as string, pv, ov]);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, ov]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -188,16 +193,16 @@ export class FalkorDBTriplesQuery {
|
|||
`RETURN rel.uri as rel, dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? []) as string[][]) {
|
||||
out.push([sv, rec[0] as string, rec[1] as string]);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await this.graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel]->(dest:Node) ` +
|
||||
`RETURN rel.uri as rel, dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? []) as string[][]) {
|
||||
out.push([sv, rec[0] as string, rec[1] as string]);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -210,16 +215,16 @@ export class FalkorDBTriplesQuery {
|
|||
`RETURN src.uri as src, dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { rel: pv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? []) as string[][]) {
|
||||
out.push([rec[0] as string, pv, rec[1] as string]);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await this.graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel {uri: $rel}]->(dest:Node) ` +
|
||||
`RETURN src.uri as src, dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { rel: pv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? []) as string[][]) {
|
||||
out.push([rec[0] as string, pv, rec[1] as string]);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, field(rec, "dest")]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,8 +238,8 @@ export class FalkorDBTriplesQuery {
|
|||
`RETURN src.uri as src, rel.uri as rel LIMIT ${limit}`,
|
||||
{ params: { dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? []) as string[][]) {
|
||||
out.push([rec[0] as string, rec[1] as string, ov]);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), ov]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -247,15 +252,15 @@ export class FalkorDBTriplesQuery {
|
|||
`MATCH (src:Node)-[rel:Rel]->(dest:Literal) ` +
|
||||
`RETURN src.uri as src, rel.uri as rel, dest.value as dest LIMIT ${limit}`,
|
||||
);
|
||||
for (const rec of (litResult.data ?? []) as string[][]) {
|
||||
out.push([rec[0] as string, rec[1] as string, rec[2] as string]);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await this.graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel]->(dest:Node) ` +
|
||||
`RETURN src.uri as src, rel.uri as rel, dest.uri as dest LIMIT ${limit}`,
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? []) as string[][]) {
|
||||
out.push([rec[0] as string, rec[1] as string, rec[2] as string]);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,8 +154,12 @@ function triplesToGraph(triples: Triple[]): {
|
|||
if (pVal === RDFS_LABEL) continue;
|
||||
if (pVal === RDF_TYPE) continue;
|
||||
|
||||
// Only build edges when both endpoints are IRIs (entity-to-entity)
|
||||
if (!isIri(t.s) || !isIri(t.o)) continue;
|
||||
// Build edges for entity-to-entity relationships.
|
||||
// Include both IRIs and literals as valid entity nodes — plain-name
|
||||
// knowledge graphs (e.g. seeded demo data) use literals for entities.
|
||||
const sIsEntity = isIri(t.s) || t.s.t === "l";
|
||||
const oIsEntity = isIri(t.o) || t.o.t === "l";
|
||||
if (!sIsEntity || !oIsEntity) continue;
|
||||
|
||||
ensureNode(sVal);
|
||||
ensureNode(oVal);
|
||||
|
|
@ -344,7 +348,7 @@ export default function GraphPage() {
|
|||
|
||||
// Build graph
|
||||
const { data: graphData, labelMap } = useMemo(
|
||||
() => triplesToGraph(triples),
|
||||
() => triplesToGraph(Array.isArray(triples) ? triples : []),
|
||||
[triples],
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue