Use MutableHashMap for workbench explain triples

This commit is contained in:
elpresidank 2026-06-04 07:49:58 -05:00
parent 338232efa8
commit fba0f97723
2 changed files with 28 additions and 9 deletions

View file

@ -2272,6 +2272,24 @@ Notes:
- `cd ts && bun run lint` - `cd ts && bun run lint`
- `git diff --check` - `git diff --check`
### 2026-06-04: Workbench Explain Triples MutableHashMap Slice
- Status: migrated and package-verified.
- Completed:
- `ts/packages/workbench/src/atoms/workbench.ts` now stores the
`explainTriplesAtom` input cache in `MutableHashMap` instead of a native
`Map`.
- Lookup narrows through `Option`, writes use `MutableHashMap.set`, and the
adjacent `Triple[]` assertions were replaced with callback return types and
inferred array copies.
- Verification:
- `cd ts && bun run --cwd packages/workbench build`
- `cd ts && bun run check:tsgo`
- `cd ts && bun run build`
- `cd ts && bun run test`
- `cd ts && bun run lint`
- `git diff --check`
## Subagent Findings To Preserve ## Subagent Findings To Preserve
- MCP/workbench: - MCP/workbench:
@ -2458,9 +2476,9 @@ Notes:
The workbench random id helper is complete; the remaining workbench The workbench random id helper is complete; the remaining workbench
`Effect.gen` match is a local one-shot command effect value. `Effect.gen` match is a local one-shot command effect value.
- Remaining real long-lived native collection targets include base processor - Remaining real long-lived native collection targets include base processor
registries, Librarian service state, and a workbench module cache. The registries and Librarian service state. The standalone Librarian collection
standalone Librarian collection manager and prompt template cache are manager, prompt template cache, and workbench explain triples module cache
complete. Local traversal sets and test fakes remain no-op boundaries. are complete. Local traversal sets and test fakes remain no-op boundaries.
## Ranked Findings ## Ranked Findings

View file

@ -3,6 +3,7 @@ import * as BrowserHttpClient from "@effect/platform-browser/BrowserHttpClient";
import * as BrowserKeyValueStore from "@effect/platform-browser/BrowserKeyValueStore"; import * as BrowserKeyValueStore from "@effect/platform-browser/BrowserKeyValueStore";
import { BaseApi, type ConnectionState, type DocumentMetadata, type ExplainEvent, type StreamingMetadata, type Term, type Triple } from "@trustgraph/client"; import { BaseApi, type ConnectionState, type DocumentMetadata, type ExplainEvent, type StreamingMetadata, type Term, type Triple } from "@trustgraph/client";
import { Cause, Clock, Context, Effect, Layer, Match, Metric, Option, Random, Schema as S } from "effect"; import { Cause, Clock, Context, Effect, Layer, Match, Metric, Option, Random, Schema as S } from "effect";
import * as MutableHashMap from "effect/MutableHashMap";
import * as Otlp from "effect/unstable/observability/Otlp"; import * as Otlp from "effect/unstable/observability/Otlp";
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
import * as Atom from "effect/unstable/reactivity/Atom"; import * as Atom from "effect/unstable/reactivity/Atom";
@ -997,7 +998,7 @@ export interface ExplainTriplesInput {
const atomFamilyKeySeparator = "\u001f"; const atomFamilyKeySeparator = "\u001f";
const explainGraphSeparator = "\u001e"; const explainGraphSeparator = "\u001e";
const explainTriplesInputs = new Map<string, ExplainTriplesInput>(); const explainTriplesInputs = MutableHashMap.empty<string, ExplainTriplesInput>();
function graphTriplesKey(input: GraphTriplesInput): string { function graphTriplesKey(input: GraphTriplesInput): string {
return [input.flowId, input.collection, String(input.limit)].join(atomFamilyKeySeparator); return [input.flowId, input.collection, String(input.limit)].join(atomFamilyKeySeparator);
@ -1041,10 +1042,10 @@ const explainTriplesAtomByKey = Atom.family((key: string) =>
queryAtom( queryAtom(
`explainTriples.${key}`, `explainTriples.${key}`,
Effect.fn("trustgraph.workbench.explainTriples")(function*(_get, api) { Effect.fn("trustgraph.workbench.explainTriples")(function*(_get, api) {
const input = explainTriplesInputs.get(key); const input = Option.getOrUndefined(MutableHashMap.get(explainTriplesInputs, key));
if (input === undefined) return []; if (input === undefined) return [];
const inlineTriples = input.events.flatMap((event) => event.explainTriples ?? []); const inlineTriples = input.events.flatMap((event): ReadonlyArray<Triple> => event.explainTriples ?? []);
if (inlineTriples.length > 0) return inlineTriples as Triple[]; if (inlineTriples.length > 0) return [...inlineTriples];
const graphUris = input.events.filter( const graphUris = input.events.filter(
(event): event is ExplainEvent & { explainGraph: string } => (event): event is ExplainEvent & { explainGraph: string } =>
event.explainGraph !== undefined && event.explainGraph.length > 0, event.explainGraph !== undefined && event.explainGraph.length > 0,
@ -1054,7 +1055,7 @@ const explainTriplesAtomByKey = Atom.family((key: string) =>
promiseBoundary(() => promiseBoundary(() =>
api.flow(input.flowId) api.flow(input.flowId)
.triplesQuery(undefined, undefined, undefined, 500, input.collection, event.explainGraph) .triplesQuery(undefined, undefined, undefined, 500, input.collection, event.explainGraph)
).pipe(Effect.orElseSucceed(() => [] as Triple[])) ).pipe(Effect.orElseSucceed((): Array<Triple> => []))
), ),
{ concurrency: 4 }, { concurrency: 4 },
); );
@ -1073,7 +1074,7 @@ const explainTriplesAtomByKey = Atom.family((key: string) =>
export const explainTriplesAtom = (input: ExplainTriplesInput) => { export const explainTriplesAtom = (input: ExplainTriplesInput) => {
const key = explainTriplesKey(input); const key = explainTriplesKey(input);
explainTriplesInputs.set(key, input); MutableHashMap.set(explainTriplesInputs, key, input);
return explainTriplesAtomByKey(key); return explainTriplesAtomByKey(key);
}; };