@trustgraph/react-state 1.7.1 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +246 -172
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +247 -174
- package/dist/index.esm.js.map +1 -1
- package/dist/state/chat-session.d.ts.map +1 -1
- package/dist/state/explainability.d.ts +8 -5
- package/dist/state/explainability.d.ts.map +1 -1
- package/dist/utils/explainability.d.ts +50 -18
- package/dist/utils/explainability.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1922,9 +1922,25 @@ const useExplainabilityStore = zustand.create()((set, get) => ({
|
|
|
1922
1922
|
/**
|
|
1923
1923
|
* Explainability utilities for parsing and structuring explain events
|
|
1924
1924
|
*/
|
|
1925
|
+
// Agent-specific predicates (not yet in client library)
|
|
1926
|
+
const TG_ACTION = client.TG + "action";
|
|
1927
|
+
const TG_ARGUMENTS = client.TG + "arguments";
|
|
1928
|
+
const TG_SUBAGENT_GOAL = client.TG + "subagentGoal";
|
|
1929
|
+
// RDF type URIs for agent events
|
|
1930
|
+
const TG_AGENT_QUESTION = client.TG + "AgentQuestion";
|
|
1931
|
+
const TG_ANALYSIS = client.TG + "Analysis";
|
|
1932
|
+
const TG_TOOL_USE = client.TG + "ToolUse";
|
|
1933
|
+
const TG_OBSERVATION = client.TG + "Observation";
|
|
1934
|
+
const TG_THOUGHT_TYPE = client.TG + "Thought";
|
|
1935
|
+
const TG_REFLECTION_TYPE = client.TG + "Reflection";
|
|
1936
|
+
const TG_CONCLUSION = client.TG + "Conclusion";
|
|
1937
|
+
const TG_ANSWER = client.TG + "Answer";
|
|
1938
|
+
const TG_FINDING = client.TG + "Finding";
|
|
1939
|
+
const TG_SYNTHESIS_TYPE = client.TG + "Synthesis";
|
|
1940
|
+
const TG_DECOMPOSITION = client.TG + "Decomposition";
|
|
1941
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
1925
1942
|
/**
|
|
1926
|
-
* Extract event type from explainId URI
|
|
1927
|
-
* e.g., "urn:trustgraph:question:abc123" → "question"
|
|
1943
|
+
* Extract event type from explainId URI (graph-rag only)
|
|
1928
1944
|
*/
|
|
1929
1945
|
function getEventType(explainId) {
|
|
1930
1946
|
if (explainId.includes("question"))
|
|
@@ -1969,9 +1985,53 @@ function extractQuotedTriple(term) {
|
|
|
1969
1985
|
}
|
|
1970
1986
|
return null;
|
|
1971
1987
|
}
|
|
1988
|
+
// ── Agent event type detection ──────────────────────────────────────
|
|
1989
|
+
/** Map RDF type URI → agent event type (first match wins) */
|
|
1990
|
+
const AGENT_TYPE_CHECKS = [
|
|
1991
|
+
[TG_AGENT_QUESTION, "agent-question"],
|
|
1992
|
+
[TG_DECOMPOSITION, "decomposition"],
|
|
1993
|
+
[TG_ANALYSIS, "analysis"],
|
|
1994
|
+
[TG_TOOL_USE, "analysis"],
|
|
1995
|
+
[TG_OBSERVATION, "reflection"],
|
|
1996
|
+
[TG_THOUGHT_TYPE, "reflection"],
|
|
1997
|
+
[TG_REFLECTION_TYPE, "reflection"],
|
|
1998
|
+
[TG_CONCLUSION, "conclusion"],
|
|
1999
|
+
[TG_FINDING, "conclusion"],
|
|
2000
|
+
[TG_SYNTHESIS_TYPE, "conclusion"],
|
|
2001
|
+
[TG_ANSWER, "conclusion"],
|
|
2002
|
+
];
|
|
1972
2003
|
/**
|
|
1973
|
-
*
|
|
2004
|
+
* Detect event type from RDF types in embedded triples.
|
|
2005
|
+
* Returns an agent event type if matched, "unknown" otherwise.
|
|
1974
2006
|
*/
|
|
2007
|
+
function getEventTypeFromTriples(triples) {
|
|
2008
|
+
const types = new Set();
|
|
2009
|
+
for (const t of triples) {
|
|
2010
|
+
if (getTermValue$1(t.p) === client.RDF_TYPE) {
|
|
2011
|
+
types.add(getTermValue$1(t.o));
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
for (const [typeUri, eventType] of AGENT_TYPE_CHECKS) {
|
|
2015
|
+
if (types.has(typeUri))
|
|
2016
|
+
return eventType;
|
|
2017
|
+
}
|
|
2018
|
+
return "unknown";
|
|
2019
|
+
}
|
|
2020
|
+
/** Extract common fields (label, derivedFrom) from triples */
|
|
2021
|
+
function extractCommonFields(triples) {
|
|
2022
|
+
const derivedFrom = [];
|
|
2023
|
+
let label;
|
|
2024
|
+
for (const t of triples) {
|
|
2025
|
+
const p = getTermValue$1(t.p);
|
|
2026
|
+
const o = getTermValue$1(t.o);
|
|
2027
|
+
if (p === client.RDFS_LABEL && o)
|
|
2028
|
+
label = o;
|
|
2029
|
+
if (p === client.PROV_WAS_DERIVED_FROM && o)
|
|
2030
|
+
derivedFrom.push(o);
|
|
2031
|
+
}
|
|
2032
|
+
return { label, derivedFrom };
|
|
2033
|
+
}
|
|
2034
|
+
// ── Graph-RAG parsers (existing) ────────────────────────────────────
|
|
1975
2035
|
function parseQuestionTriples(explainId, explainGraph, triples) {
|
|
1976
2036
|
const event = {
|
|
1977
2037
|
type: "question",
|
|
@@ -1990,9 +2050,6 @@ function parseQuestionTriples(explainId, explainGraph, triples) {
|
|
|
1990
2050
|
}
|
|
1991
2051
|
return event;
|
|
1992
2052
|
}
|
|
1993
|
-
/**
|
|
1994
|
-
* Parse triples for an exploration event
|
|
1995
|
-
*/
|
|
1996
2053
|
function parseExplorationTriples(explainId, explainGraph, triples) {
|
|
1997
2054
|
const event = {
|
|
1998
2055
|
type: "exploration",
|
|
@@ -2008,9 +2065,6 @@ function parseExplorationTriples(explainId, explainGraph, triples) {
|
|
|
2008
2065
|
}
|
|
2009
2066
|
return event;
|
|
2010
2067
|
}
|
|
2011
|
-
/**
|
|
2012
|
-
* Parse triples for a focus event
|
|
2013
|
-
*/
|
|
2014
2068
|
function parseFocusTriples(explainId, explainGraph, triples) {
|
|
2015
2069
|
const event = {
|
|
2016
2070
|
type: "focus",
|
|
@@ -2027,9 +2081,6 @@ function parseFocusTriples(explainId, explainGraph, triples) {
|
|
|
2027
2081
|
}
|
|
2028
2082
|
return event;
|
|
2029
2083
|
}
|
|
2030
|
-
/**
|
|
2031
|
-
* Parse triples for a synthesis event
|
|
2032
|
-
*/
|
|
2033
2084
|
function parseSynthesisTriples(explainId, explainGraph, triples) {
|
|
2034
2085
|
const event = {
|
|
2035
2086
|
type: "synthesis",
|
|
@@ -2045,9 +2096,6 @@ function parseSynthesisTriples(explainId, explainGraph, triples) {
|
|
|
2045
2096
|
}
|
|
2046
2097
|
return event;
|
|
2047
2098
|
}
|
|
2048
|
-
/**
|
|
2049
|
-
* Parse triples for an edge selection entity
|
|
2050
|
-
*/
|
|
2051
2099
|
function parseEdgeSelectionTriples(triples) {
|
|
2052
2100
|
let edge = null;
|
|
2053
2101
|
let reasoning = null;
|
|
@@ -2062,10 +2110,109 @@ function parseEdgeSelectionTriples(triples) {
|
|
|
2062
2110
|
}
|
|
2063
2111
|
return { edge, reasoning };
|
|
2064
2112
|
}
|
|
2113
|
+
// ── Agent parsers (new) ─────────────────────────────────────────────
|
|
2114
|
+
function parseAgentQuestionTriples(explainId, explainGraph, triples) {
|
|
2115
|
+
const { label, derivedFrom } = extractCommonFields(triples);
|
|
2116
|
+
const event = {
|
|
2117
|
+
type: "agent-question",
|
|
2118
|
+
explainId,
|
|
2119
|
+
explainGraph,
|
|
2120
|
+
label,
|
|
2121
|
+
derivedFrom,
|
|
2122
|
+
};
|
|
2123
|
+
for (const t of triples) {
|
|
2124
|
+
const p = getTermValue$1(t.p);
|
|
2125
|
+
const o = getTermValue$1(t.o);
|
|
2126
|
+
if (p === client.TG_QUERY)
|
|
2127
|
+
event.query = o;
|
|
2128
|
+
if (p === client.PROV_STARTED_AT_TIME)
|
|
2129
|
+
event.timestamp = o;
|
|
2130
|
+
}
|
|
2131
|
+
return event;
|
|
2132
|
+
}
|
|
2133
|
+
function parseDecompositionTriples(explainId, explainGraph, triples) {
|
|
2134
|
+
const { label, derivedFrom } = extractCommonFields(triples);
|
|
2135
|
+
const goals = [];
|
|
2136
|
+
for (const t of triples) {
|
|
2137
|
+
const p = getTermValue$1(t.p);
|
|
2138
|
+
const o = getTermValue$1(t.o);
|
|
2139
|
+
if (p === TG_SUBAGENT_GOAL && o)
|
|
2140
|
+
goals.push(o);
|
|
2141
|
+
}
|
|
2142
|
+
return {
|
|
2143
|
+
type: "decomposition",
|
|
2144
|
+
explainId,
|
|
2145
|
+
explainGraph,
|
|
2146
|
+
label,
|
|
2147
|
+
goals,
|
|
2148
|
+
derivedFrom,
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
function parseAnalysisTriples(explainId, explainGraph, triples) {
|
|
2152
|
+
const { label, derivedFrom } = extractCommonFields(triples);
|
|
2153
|
+
const event = {
|
|
2154
|
+
type: "analysis",
|
|
2155
|
+
explainId,
|
|
2156
|
+
explainGraph,
|
|
2157
|
+
label,
|
|
2158
|
+
derivedFrom,
|
|
2159
|
+
};
|
|
2160
|
+
for (const t of triples) {
|
|
2161
|
+
const p = getTermValue$1(t.p);
|
|
2162
|
+
const o = getTermValue$1(t.o);
|
|
2163
|
+
if (p === TG_ACTION)
|
|
2164
|
+
event.action = o;
|
|
2165
|
+
if (p === TG_ARGUMENTS)
|
|
2166
|
+
event.arguments = o;
|
|
2167
|
+
}
|
|
2168
|
+
return event;
|
|
2169
|
+
}
|
|
2170
|
+
function parseReflectionTriples(explainId, explainGraph, triples) {
|
|
2171
|
+
const { label, derivedFrom } = extractCommonFields(triples);
|
|
2172
|
+
return {
|
|
2173
|
+
type: "reflection",
|
|
2174
|
+
explainId,
|
|
2175
|
+
explainGraph,
|
|
2176
|
+
label,
|
|
2177
|
+
derivedFrom,
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
function parseConclusionTriples(explainId, explainGraph, triples) {
|
|
2181
|
+
const { label, derivedFrom } = extractCommonFields(triples);
|
|
2182
|
+
return {
|
|
2183
|
+
type: "conclusion",
|
|
2184
|
+
explainId,
|
|
2185
|
+
explainGraph,
|
|
2186
|
+
label,
|
|
2187
|
+
derivedFrom,
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
// ── Unified parser ──────────────────────────────────────────────────
|
|
2065
2191
|
/**
|
|
2066
|
-
* Parse triples
|
|
2192
|
+
* Parse triples into a structured event.
|
|
2193
|
+
* Tries RDF type detection first (agent events), then URI patterns (graph-rag).
|
|
2194
|
+
* Returns null for events with no triples (inner graph-rag plumbing).
|
|
2067
2195
|
*/
|
|
2068
2196
|
function parseExplainTriples(explainId, explainGraph, triples) {
|
|
2197
|
+
if (triples.length === 0)
|
|
2198
|
+
return null;
|
|
2199
|
+
// Try RDF type detection first (agent events with embedded triples)
|
|
2200
|
+
const rdfEventType = getEventTypeFromTriples(triples);
|
|
2201
|
+
if (rdfEventType !== "unknown") {
|
|
2202
|
+
switch (rdfEventType) {
|
|
2203
|
+
case "agent-question":
|
|
2204
|
+
return parseAgentQuestionTriples(explainId, explainGraph, triples);
|
|
2205
|
+
case "decomposition":
|
|
2206
|
+
return parseDecompositionTriples(explainId, explainGraph, triples);
|
|
2207
|
+
case "analysis":
|
|
2208
|
+
return parseAnalysisTriples(explainId, explainGraph, triples);
|
|
2209
|
+
case "reflection":
|
|
2210
|
+
return parseReflectionTriples(explainId, explainGraph, triples);
|
|
2211
|
+
case "conclusion":
|
|
2212
|
+
return parseConclusionTriples(explainId, explainGraph, triples);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
// Fall back to URI pattern detection (graph-rag events)
|
|
2069
2216
|
const eventType = getEventType(explainId);
|
|
2070
2217
|
switch (eventType) {
|
|
2071
2218
|
case "question":
|
|
@@ -2252,24 +2399,26 @@ const useProvenance = (options = {}) => {
|
|
|
2252
2399
|
*
|
|
2253
2400
|
* Processing strategy:
|
|
2254
2401
|
* - Events are processed immediately as they arrive
|
|
2255
|
-
* -
|
|
2256
|
-
*
|
|
2257
|
-
* - Edge
|
|
2258
|
-
* -
|
|
2402
|
+
* - Explain triples are embedded directly in events (no store lookups)
|
|
2403
|
+
* - Edge labels are resolved via triple store queries
|
|
2404
|
+
* - Edge label resolution + provenance runs in parallel
|
|
2405
|
+
* - onUpdate callback fires on every session state change, allowing
|
|
2406
|
+
* callers to sync to external stores (e.g. Zustand)
|
|
2259
2407
|
*/
|
|
2260
2408
|
/**
|
|
2261
2409
|
* Hook for managing explainability during inference
|
|
2262
2410
|
*/
|
|
2263
2411
|
const useExplainability = (options = {}) => {
|
|
2264
2412
|
const { flow, collection = "default", traceProvenance = true, } = options;
|
|
2265
|
-
const socket = reactProvider.useSocket();
|
|
2266
|
-
const connectionState = reactProvider.useConnectionState();
|
|
2267
2413
|
const sessionFlowId = useSessionStore((state) => state.flowId);
|
|
2268
2414
|
const effectiveFlow = flow ?? sessionFlowId;
|
|
2269
2415
|
const { traceEdgeProvenance, resolveLabel } = useProvenance({
|
|
2270
2416
|
flow: effectiveFlow,
|
|
2271
2417
|
collection,
|
|
2272
2418
|
});
|
|
2419
|
+
// Keep onUpdate in a ref so it doesn't break memoisation
|
|
2420
|
+
const onUpdateRef = react.useRef(options.onUpdate);
|
|
2421
|
+
onUpdateRef.current = options.onUpdate;
|
|
2273
2422
|
const [events, setEvents] = react.useState([]);
|
|
2274
2423
|
const [session, setSession] = react.useState({});
|
|
2275
2424
|
const [isUnpacking, setIsUnpacking] = react.useState(false);
|
|
@@ -2280,98 +2429,19 @@ const useExplainability = (options = {}) => {
|
|
|
2280
2429
|
const unpackQueueRef = react.useRef([]);
|
|
2281
2430
|
const isProcessingRef = react.useRef(false);
|
|
2282
2431
|
/**
|
|
2283
|
-
*
|
|
2284
|
-
*/
|
|
2285
|
-
const isConnected = react.useCallback(() => {
|
|
2286
|
-
return (connectionState?.status === "authenticated" ||
|
|
2287
|
-
connectionState?.status === "unauthenticated");
|
|
2288
|
-
}, [connectionState]);
|
|
2289
|
-
/**
|
|
2290
|
-
* Single triple query (no retry)
|
|
2291
|
-
*/
|
|
2292
|
-
const fetchTriples = react.useCallback(async (explainId, explainGraph) => {
|
|
2293
|
-
return socket
|
|
2294
|
-
.flow(effectiveFlow)
|
|
2295
|
-
.triplesQuery({ t: "i", i: explainId }, undefined, undefined, 100, collection, explainGraph);
|
|
2296
|
-
}, [socket, effectiveFlow, collection]);
|
|
2297
|
-
/**
|
|
2298
|
-
* Stability-based retry for main event nodes.
|
|
2299
|
-
* Retries until: count > 0 AND count matches previous fetch.
|
|
2300
|
-
* Used for question, exploration, focus, synthesis nodes where the
|
|
2301
|
-
* backend may write multiple triples incrementally.
|
|
2302
|
-
*/
|
|
2303
|
-
const queryWithStabilityRetry = react.useCallback(async (explainId, explainGraph, timeoutMs = 5000) => {
|
|
2304
|
-
if (!isConnected())
|
|
2305
|
-
return [];
|
|
2306
|
-
const retryDelay = 500;
|
|
2307
|
-
const maxAttempts = Math.ceil(timeoutMs / retryDelay) + 1;
|
|
2308
|
-
let prevCount = -1;
|
|
2309
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2310
|
-
try {
|
|
2311
|
-
const triples = await fetchTriples(explainId, explainGraph);
|
|
2312
|
-
const count = triples.length;
|
|
2313
|
-
if (count > 0 && count === prevCount) {
|
|
2314
|
-
return triples;
|
|
2315
|
-
}
|
|
2316
|
-
prevCount = count;
|
|
2317
|
-
if (attempt < maxAttempts - 1) {
|
|
2318
|
-
await new Promise((r) => setTimeout(r, retryDelay));
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
catch (err) {
|
|
2322
|
-
console.error("[explain] triple query failed:", explainId, err);
|
|
2323
|
-
return [];
|
|
2324
|
-
}
|
|
2325
|
-
}
|
|
2326
|
-
// Return last fetch if we got anything
|
|
2327
|
-
if (prevCount > 0) {
|
|
2328
|
-
try {
|
|
2329
|
-
return await fetchTriples(explainId, explainGraph);
|
|
2330
|
-
}
|
|
2331
|
-
catch {
|
|
2332
|
-
return [];
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
return [];
|
|
2336
|
-
}, [fetchTriples, isConnected]);
|
|
2337
|
-
/**
|
|
2338
|
-
* Simple retry-until-non-empty for sub-objects (edge selections).
|
|
2339
|
-
* These are small atomic writes — either fully there or not yet.
|
|
2340
|
-
*/
|
|
2341
|
-
const queryWithSimpleRetry = react.useCallback(async (explainId, explainGraph, timeoutMs = 5000) => {
|
|
2342
|
-
if (!isConnected())
|
|
2343
|
-
return [];
|
|
2344
|
-
const retryDelay = 300;
|
|
2345
|
-
const maxAttempts = Math.ceil(timeoutMs / retryDelay) + 1;
|
|
2346
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2347
|
-
try {
|
|
2348
|
-
const triples = await fetchTriples(explainId, explainGraph);
|
|
2349
|
-
if (triples.length > 0)
|
|
2350
|
-
return triples;
|
|
2351
|
-
if (attempt < maxAttempts - 1) {
|
|
2352
|
-
await new Promise((r) => setTimeout(r, retryDelay));
|
|
2353
|
-
}
|
|
2354
|
-
}
|
|
2355
|
-
catch (err) {
|
|
2356
|
-
console.error("[explain] triple query failed:", explainId, err);
|
|
2357
|
-
return [];
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
return [];
|
|
2361
|
-
}, [fetchTriples, isConnected]);
|
|
2362
|
-
/**
|
|
2363
|
-
* Resolve a single edge: fetch triples, labels, and provenance in parallel
|
|
2432
|
+
* Resolve a single edge: extract from embedded triples, resolve labels
|
|
2364
2433
|
*/
|
|
2365
|
-
const resolveEdge = react.useCallback(async (edgeSelUri,
|
|
2366
|
-
|
|
2367
|
-
const
|
|
2434
|
+
const resolveEdge = react.useCallback(async (edgeSelUri, allTriples) => {
|
|
2435
|
+
// Filter embedded triples for this edge selection URI
|
|
2436
|
+
const edgeTriples = allTriples.filter((t) => getTermValue$1(t.s) === edgeSelUri);
|
|
2437
|
+
const { edge, reasoning } = parseEdgeSelectionTriples(edgeTriples);
|
|
2368
2438
|
if (!edge)
|
|
2369
2439
|
return null;
|
|
2370
2440
|
const selectedEdge = {
|
|
2371
2441
|
edge,
|
|
2372
2442
|
reasoning: reasoning || undefined,
|
|
2373
2443
|
};
|
|
2374
|
-
// Kick off
|
|
2444
|
+
// Kick off label resolution and provenance in parallel
|
|
2375
2445
|
const [labels, provenanceChains] = await Promise.all([
|
|
2376
2446
|
// Labels — all 3 in parallel
|
|
2377
2447
|
Promise.all([
|
|
@@ -2389,13 +2459,13 @@ const useExplainability = (options = {}) => {
|
|
|
2389
2459
|
selectedEdge.sources = provenanceChains.map((c) => c.chain).flat();
|
|
2390
2460
|
}
|
|
2391
2461
|
return selectedEdge;
|
|
2392
|
-
}, [
|
|
2462
|
+
}, [resolveLabel, traceProvenance, traceEdgeProvenance]);
|
|
2393
2463
|
/**
|
|
2394
2464
|
* Unpack a focus event — resolve ALL edges in parallel
|
|
2395
2465
|
*/
|
|
2396
|
-
const unpackFocusEvent = react.useCallback(async (focusEvent) => {
|
|
2466
|
+
const unpackFocusEvent = react.useCallback(async (focusEvent, allTriples) => {
|
|
2397
2467
|
// Fire off all edge resolutions concurrently
|
|
2398
|
-
const edgePromises = focusEvent.edgeSelectionUris.map((uri) => resolveEdge(uri,
|
|
2468
|
+
const edgePromises = focusEvent.edgeSelectionUris.map((uri) => resolveEdge(uri, allTriples));
|
|
2399
2469
|
const results = await Promise.all(edgePromises);
|
|
2400
2470
|
const selectedEdges = results.filter((e) => e !== null);
|
|
2401
2471
|
return {
|
|
@@ -2403,50 +2473,62 @@ const useExplainability = (options = {}) => {
|
|
|
2403
2473
|
selectedEdges,
|
|
2404
2474
|
};
|
|
2405
2475
|
}, [resolveEdge]);
|
|
2406
|
-
/** Helper to update
|
|
2476
|
+
/** Helper to update state, ref, and notify caller together */
|
|
2407
2477
|
const updateSession = react.useCallback((updater) => {
|
|
2408
2478
|
sessionRef.current = updater(sessionRef.current);
|
|
2409
2479
|
setSession(updater);
|
|
2480
|
+
onUpdateRef.current?.(sessionRef.current);
|
|
2410
2481
|
}, []);
|
|
2482
|
+
const AGENT_EVENT_TYPES = new Set([
|
|
2483
|
+
"agent-question", "decomposition", "analysis", "reflection", "conclusion",
|
|
2484
|
+
]);
|
|
2411
2485
|
/**
|
|
2412
2486
|
* Process a single explain event
|
|
2413
2487
|
*/
|
|
2414
2488
|
const processEvent = react.useCallback(async (event) => {
|
|
2415
|
-
//
|
|
2416
|
-
const triples =
|
|
2489
|
+
// Use embedded triples directly from the event
|
|
2490
|
+
const triples = event.explainTriples ?? [];
|
|
2417
2491
|
// Parse into structured data
|
|
2418
2492
|
const parsed = parseExplainTriples(event.explainId, event.explainGraph, triples);
|
|
2419
2493
|
if (!parsed)
|
|
2420
2494
|
return;
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
const next = { ...prev };
|
|
2424
|
-
switch (parsed.type) {
|
|
2425
|
-
case "question":
|
|
2426
|
-
next.question = parsed;
|
|
2427
|
-
break;
|
|
2428
|
-
case "exploration":
|
|
2429
|
-
next.exploration = parsed;
|
|
2430
|
-
break;
|
|
2431
|
-
case "focus":
|
|
2432
|
-
// Will be updated again after unpacking
|
|
2433
|
-
next.focus = parsed;
|
|
2434
|
-
break;
|
|
2435
|
-
case "synthesis":
|
|
2436
|
-
next.synthesis = parsed;
|
|
2437
|
-
break;
|
|
2438
|
-
}
|
|
2439
|
-
return next;
|
|
2440
|
-
});
|
|
2441
|
-
// For focus events, unpack edges in parallel
|
|
2442
|
-
if (parsed.type === "focus") {
|
|
2443
|
-
const unpackedFocus = await unpackFocusEvent(parsed);
|
|
2495
|
+
if (AGENT_EVENT_TYPES.has(parsed.type)) {
|
|
2496
|
+
// Agent event — append to timeline
|
|
2444
2497
|
updateSession((prev) => ({
|
|
2445
2498
|
...prev,
|
|
2446
|
-
|
|
2499
|
+
agentSteps: [...(prev.agentSteps || []), parsed],
|
|
2447
2500
|
}));
|
|
2448
2501
|
}
|
|
2449
|
-
|
|
2502
|
+
else {
|
|
2503
|
+
// Graph-RAG event — populate fixed fields
|
|
2504
|
+
updateSession((prev) => {
|
|
2505
|
+
const next = { ...prev };
|
|
2506
|
+
switch (parsed.type) {
|
|
2507
|
+
case "question":
|
|
2508
|
+
next.question = parsed;
|
|
2509
|
+
break;
|
|
2510
|
+
case "exploration":
|
|
2511
|
+
next.exploration = parsed;
|
|
2512
|
+
break;
|
|
2513
|
+
case "focus":
|
|
2514
|
+
next.focus = parsed;
|
|
2515
|
+
break;
|
|
2516
|
+
case "synthesis":
|
|
2517
|
+
next.synthesis = parsed;
|
|
2518
|
+
break;
|
|
2519
|
+
}
|
|
2520
|
+
return next;
|
|
2521
|
+
});
|
|
2522
|
+
// For focus events, unpack edges (labels still need store lookup)
|
|
2523
|
+
if (parsed.type === "focus") {
|
|
2524
|
+
const unpackedFocus = await unpackFocusEvent(parsed, triples);
|
|
2525
|
+
updateSession((prev) => ({
|
|
2526
|
+
...prev,
|
|
2527
|
+
focus: unpackedFocus,
|
|
2528
|
+
}));
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}, [unpackFocusEvent, updateSession]);
|
|
2450
2532
|
/**
|
|
2451
2533
|
* Process the unpack queue
|
|
2452
2534
|
*/
|
|
@@ -2529,10 +2611,18 @@ const useChatSession = ({ flow } = {}) => {
|
|
|
2529
2611
|
const { settings } = useSettings();
|
|
2530
2612
|
// Explainability store for persisting sessions
|
|
2531
2613
|
const addExplainSession = useExplainabilityStore((state) => state.addSession);
|
|
2532
|
-
//
|
|
2614
|
+
// Track the current explain session ID so onUpdate can target the right store key
|
|
2615
|
+
const explainSessionIdRef = react.useRef(undefined);
|
|
2616
|
+
// Explainability hook — onUpdate syncs every state change to the Zustand store
|
|
2533
2617
|
const explainability = useExplainability({
|
|
2534
2618
|
flow: effectiveFlow,
|
|
2535
2619
|
collection: settings.collection,
|
|
2620
|
+
onUpdate: (session) => {
|
|
2621
|
+
const id = explainSessionIdRef.current;
|
|
2622
|
+
if (id) {
|
|
2623
|
+
addExplainSession(id, session);
|
|
2624
|
+
}
|
|
2625
|
+
},
|
|
2536
2626
|
});
|
|
2537
2627
|
const explainabilityRef = react.useRef(explainability);
|
|
2538
2628
|
explainabilityRef.current = explainability;
|
|
@@ -2551,13 +2641,9 @@ const useChatSession = ({ flow } = {}) => {
|
|
|
2551
2641
|
addActivity(ragActivity);
|
|
2552
2642
|
let accumulated = "";
|
|
2553
2643
|
let messageAdded = false;
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
// Reset explainability state for new query
|
|
2558
|
-
if (explainabilityEnabled) {
|
|
2559
|
-
explainabilityRef.current.reset();
|
|
2560
|
-
}
|
|
2644
|
+
const sessionId = generateSessionId();
|
|
2645
|
+
explainabilityRef.current.reset();
|
|
2646
|
+
explainSessionIdRef.current = sessionId;
|
|
2561
2647
|
try {
|
|
2562
2648
|
// Execute Graph RAG with streaming and entity discovery
|
|
2563
2649
|
const result = await inference.graphRag({
|
|
@@ -2573,41 +2659,20 @@ const useChatSession = ({ flow } = {}) => {
|
|
|
2573
2659
|
onChunk: (chunk, complete) => {
|
|
2574
2660
|
accumulated += chunk;
|
|
2575
2661
|
if (!messageAdded) {
|
|
2576
|
-
// Add empty message on first chunk (with session ID if enabled)
|
|
2577
2662
|
addMessage("ai", accumulated, undefined, sessionId);
|
|
2578
2663
|
messageAdded = true;
|
|
2579
2664
|
}
|
|
2580
2665
|
else {
|
|
2581
|
-
// Update existing message with accumulated text
|
|
2582
2666
|
updateLastMessage(accumulated);
|
|
2583
2667
|
}
|
|
2584
2668
|
},
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
explainabilityRef.current.addEvent(event);
|
|
2590
|
-
},
|
|
2591
|
-
}),
|
|
2669
|
+
onExplain: (event) => {
|
|
2670
|
+
console.log("[explain] event received:", event.explainId);
|
|
2671
|
+
explainabilityRef.current.addEvent(event);
|
|
2672
|
+
},
|
|
2592
2673
|
},
|
|
2593
2674
|
});
|
|
2594
2675
|
removeActivity(ragActivity);
|
|
2595
|
-
// Store explainability session progressively — events are already being
|
|
2596
|
-
// processed as they arrive. Wait for processing to finish, then snapshot.
|
|
2597
|
-
if (explainabilityEnabled && sessionId) {
|
|
2598
|
-
const waitAndStore = async () => {
|
|
2599
|
-
// Poll until processing completes (events are processed as they arrive)
|
|
2600
|
-
const maxWait = 30000;
|
|
2601
|
-
let elapsed = 0;
|
|
2602
|
-
while (explainabilityRef.current.isProcessingRef.current && elapsed < maxWait) {
|
|
2603
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
2604
|
-
elapsed += 500;
|
|
2605
|
-
}
|
|
2606
|
-
const sess = explainabilityRef.current.sessionRef.current;
|
|
2607
|
-
addExplainSession(sessionId, sess);
|
|
2608
|
-
};
|
|
2609
|
-
waitAndStore();
|
|
2610
|
-
}
|
|
2611
2676
|
// Start embeddings activity
|
|
2612
2677
|
addActivity(embActivity);
|
|
2613
2678
|
// Get labels for each entity
|
|
@@ -2695,9 +2760,13 @@ const useChatSession = ({ flow } = {}) => {
|
|
|
2695
2760
|
let observationMessageAdded = false;
|
|
2696
2761
|
let answerAccumulated = "";
|
|
2697
2762
|
let answerMessageAdded = false;
|
|
2763
|
+
const sessionId = generateSessionId();
|
|
2764
|
+
explainabilityRef.current.reset();
|
|
2765
|
+
explainSessionIdRef.current = sessionId;
|
|
2698
2766
|
try {
|
|
2699
2767
|
const response = await inference.agent({
|
|
2700
2768
|
input,
|
|
2769
|
+
collection: settings.collection,
|
|
2701
2770
|
callbacks: {
|
|
2702
2771
|
onThink: (thought, complete) => {
|
|
2703
2772
|
thinkingAccumulated += thought;
|
|
@@ -2730,13 +2799,17 @@ const useChatSession = ({ flow } = {}) => {
|
|
|
2730
2799
|
onAnswer: (answer, complete) => {
|
|
2731
2800
|
answerAccumulated += answer;
|
|
2732
2801
|
if (!answerMessageAdded) {
|
|
2733
|
-
addMessage("ai", answerAccumulated, "answer");
|
|
2802
|
+
addMessage("ai", answerAccumulated, "answer", sessionId);
|
|
2734
2803
|
answerMessageAdded = true;
|
|
2735
2804
|
}
|
|
2736
2805
|
else {
|
|
2737
2806
|
updateLastMessage(answerAccumulated);
|
|
2738
2807
|
}
|
|
2739
2808
|
},
|
|
2809
|
+
onExplain: (event) => {
|
|
2810
|
+
console.log("[explain] agent event received:", event.explainId);
|
|
2811
|
+
explainabilityRef.current.addEvent(event);
|
|
2812
|
+
},
|
|
2740
2813
|
},
|
|
2741
2814
|
});
|
|
2742
2815
|
removeActivity(activity);
|
|
@@ -6093,6 +6166,7 @@ exports.extractQuotedTriple = extractQuotedTriple;
|
|
|
6093
6166
|
exports.fileToBase64 = fileToBase64;
|
|
6094
6167
|
exports.generateFlowBlueprintId = generateFlowBlueprintId;
|
|
6095
6168
|
exports.getEventType = getEventType;
|
|
6169
|
+
exports.getEventTypeFromTriples = getEventTypeFromTriples;
|
|
6096
6170
|
exports.getExplainTermValue = getTermValue$1;
|
|
6097
6171
|
exports.getTermValue = getTermValue$3;
|
|
6098
6172
|
exports.getTriples = getTriples;
|