@odla-ai/kg 0.3.0 → 0.3.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.
@@ -20,6 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/temporal/index.ts
21
21
  var temporal_exports = {};
22
22
  __export(temporal_exports, {
23
+ blendPairs: () => blendPairs,
24
+ bridgingScores: () => bridgingScores,
23
25
  bucketFor: () => bucketFor,
24
26
  bucketKey: () => bucketKey,
25
27
  bucketRange: () => bucketRange,
@@ -28,21 +30,28 @@ __export(temporal_exports, {
28
30
  buildTimeline: () => buildTimeline,
29
31
  burstScores: () => burstScores,
30
32
  byNodeId: () => byNodeId,
33
+ composeTargets: () => composeTargets,
31
34
  detectBursts: () => detectBursts,
35
+ directTargets: () => directTargets,
32
36
  extractEvents: () => extractEvents,
33
37
  flowFrames: () => flowFrames,
34
38
  forceLayout: () => forceLayout,
39
+ geoPairs: () => geoPairs,
35
40
  hash32: () => hash32,
36
41
  indexGraph: () => indexGraph,
37
42
  labelPropagation: () => labelPropagation,
38
43
  layoutSequence: () => layoutSequence,
39
44
  leadScores: () => leadScores,
45
+ lensSnapshots: () => lensSnapshots,
46
+ matchRules: () => matchRules,
40
47
  mulberry32: () => mulberry32,
41
48
  nodeId: () => nodeId,
42
49
  parseAmount: () => parseAmount,
43
50
  parseDateMs: () => parseDateMs,
51
+ projectBridges: () => projectBridges,
44
52
  rankEnrichmentCandidates: () => rankEnrichmentCandidates,
45
53
  rankHotspots: () => rankHotspots,
54
+ reifiedBridgeTargets: () => reifiedBridgeTargets,
46
55
  resolvePath: () => resolvePath,
47
56
  rollup: () => rollup,
48
57
  seriesKeys: () => seriesKeys,
@@ -50,8 +59,11 @@ __export(temporal_exports, {
50
59
  shuffle: () => shuffle,
51
60
  snapshotAt: () => snapshotAt,
52
61
  snapshotSeries: () => snapshotSeries,
62
+ spatialFindings: () => spatialFindings,
53
63
  splitTitleBody: () => splitTitleBody,
54
64
  synthesizeBurstSources: () => synthesizeBurstSources,
65
+ timePairs: () => timePairs,
66
+ topBridges: () => topBridges,
55
67
  topFlows: () => topFlows,
56
68
  totals: () => totals,
57
69
  trackClusters: () => trackClusters
@@ -878,4 +890,197 @@ function flowFrames(events, opts) {
878
890
  function topFlows(frame, n) {
879
891
  return frame.flows.slice(0, n);
880
892
  }
893
+
894
+ // src/temporal/spatial.ts
895
+ function matchRules(values, rules) {
896
+ if (!values) return [];
897
+ return rules.filter((r) => {
898
+ const v = values[r.metric];
899
+ if (v === void 0) return false;
900
+ return r.op === ">=" ? v >= r.threshold : v <= r.threshold;
901
+ }).map((r) => r.id);
902
+ }
903
+ function spatialFindings(set, metrics, rules, opts = {}) {
904
+ const { top = 20, requireMatch = false } = opts;
905
+ const best = /* @__PURE__ */ new Map();
906
+ for (const burst of detectBursts(set, opts)) {
907
+ if (!best.has(burst.seriesKey)) best.set(burst.seriesKey, burst);
908
+ }
909
+ const findings = [];
910
+ for (const [region, burst] of best) {
911
+ const values = metrics[region];
912
+ const matched = matchRules(values, rules);
913
+ if (requireMatch && matched.length === 0) continue;
914
+ const boost = rules.filter((r) => matched.includes(r.id)).reduce((a, r) => a + (r.weight ?? 1), 0);
915
+ const evidence = {};
916
+ for (const r of rules) {
917
+ const v = values?.[r.metric];
918
+ if (v !== void 0) evidence[r.metric] = v;
919
+ }
920
+ findings.push({ region, burst, matched, score: burst.score * (1 + boost), metrics: evidence });
921
+ }
922
+ return findings.sort((a, b) => b.score - a.score || (a.region < b.region ? -1 : 1)).slice(0, top);
923
+ }
924
+
925
+ // src/temporal/lenses.ts
926
+ function reifiedBridgeTargets(fragment, opts) {
927
+ const bridgesOf = /* @__PURE__ */ new Map();
928
+ const targetsOf = /* @__PURE__ */ new Map();
929
+ for (const e of fragment.edges) {
930
+ if (e.from.type !== opts.reified) continue;
931
+ const to = `${e.to.type}:${e.to.key}`;
932
+ if (e.label === opts.bridgeLabel) (bridgesOf.get(e.from.key) ?? bridgesOf.set(e.from.key, []).get(e.from.key)).push(to);
933
+ if (e.label === opts.targetLabel) (targetsOf.get(e.from.key) ?? targetsOf.set(e.from.key, []).get(e.from.key)).push(to);
934
+ }
935
+ const out = /* @__PURE__ */ new Map();
936
+ for (const [reifiedKey, bridges] of bridgesOf) {
937
+ for (const bridge of bridges) {
938
+ for (const target of targetsOf.get(reifiedKey) ?? []) {
939
+ (out.get(bridge) ?? out.set(bridge, /* @__PURE__ */ new Set()).get(bridge)).add(target);
940
+ }
941
+ }
942
+ }
943
+ return out;
944
+ }
945
+ function directTargets(fragment, label) {
946
+ const out = /* @__PURE__ */ new Map();
947
+ for (const e of fragment.edges) {
948
+ if (e.label !== label) continue;
949
+ const from = `${e.from.type}:${e.from.key}`;
950
+ (out.get(from) ?? out.set(from, /* @__PURE__ */ new Set()).get(from)).add(`${e.to.type}:${e.to.key}`);
951
+ }
952
+ return out;
953
+ }
954
+ function composeTargets(ab, bc) {
955
+ const out = /* @__PURE__ */ new Map();
956
+ for (const [a, bs] of ab) {
957
+ for (const b of bs) {
958
+ for (const c of bc.get(b) ?? []) {
959
+ (out.get(a) ?? out.set(a, /* @__PURE__ */ new Set()).get(a)).add(c);
960
+ }
961
+ }
962
+ }
963
+ return out;
964
+ }
965
+ function projectBridges(bridges, opts = {}) {
966
+ const { maxPerBridge = 12, viaCap = 4 } = opts;
967
+ const acc = /* @__PURE__ */ new Map();
968
+ for (const [bridge, targetSet] of [...bridges.entries()].sort((x, y) => x[0] < y[0] ? -1 : 1)) {
969
+ const targets = [...targetSet].sort();
970
+ if (targets.length < 2 || targets.length > maxPerBridge) continue;
971
+ for (let i = 0; i < targets.length; i++) {
972
+ for (let j = i + 1; j < targets.length; j++) {
973
+ const key = `${targets[i]}|${targets[j]}`;
974
+ const hit = acc.get(key) ?? { a: targets[i], b: targets[j], w: 0, via: [] };
975
+ hit.w += 1;
976
+ if (hit.via.length < viaCap) hit.via.push(bridge);
977
+ acc.set(key, hit);
978
+ }
979
+ }
980
+ }
981
+ return [...acc.keys()].sort().map((k) => acc.get(k));
982
+ }
983
+ function timePairs(arrivals, opts = {}) {
984
+ const { k = 4, halfLifeDays = 30 } = opts;
985
+ const halfLife = halfLifeDays * 864e5;
986
+ const rows = [...arrivals.entries()].filter(([, t]) => Number.isFinite(t)).sort((x, y) => x[1] - y[1] || (x[0] < y[0] ? -1 : 1));
987
+ const acc = /* @__PURE__ */ new Map();
988
+ for (let i = 0; i < rows.length; i++) {
989
+ for (let j = i + 1; j < Math.min(rows.length, i + 1 + k); j++) {
990
+ const [idA, tA] = rows[i];
991
+ const [idB, tB] = rows[j];
992
+ const w = Math.pow(0.5, Math.abs(tB - tA) / halfLife);
993
+ if (w < 0.01) break;
994
+ const [a, b] = idA < idB ? [idA, idB] : [idB, idA];
995
+ acc.set(`${a}|${b}`, { a, b, w, via: [] });
996
+ }
997
+ }
998
+ return [...acc.keys()].sort().map((key) => acc.get(key));
999
+ }
1000
+ function geoPairs(positions, opts = {}) {
1001
+ const { k = 4, halfDist = 0.04 } = opts;
1002
+ const rows = [...positions.entries()].sort((x, y) => x[0] < y[0] ? -1 : 1);
1003
+ const acc = /* @__PURE__ */ new Map();
1004
+ for (let i = 0; i < rows.length; i++) {
1005
+ const dists = [];
1006
+ for (let j = 0; j < rows.length; j++) {
1007
+ if (i === j) continue;
1008
+ const dx = rows[i][1].x - rows[j][1].x;
1009
+ const dy = rows[i][1].y - rows[j][1].y;
1010
+ dists.push({ j, d: Math.hypot(dx, dy) });
1011
+ }
1012
+ dists.sort((x, y) => x.d - y.d || x.j - y.j);
1013
+ for (const { j, d } of dists.slice(0, k)) {
1014
+ const w = Math.pow(0.5, d / halfDist);
1015
+ if (w < 0.01) continue;
1016
+ const [a, b] = rows[i][0] < rows[j][0] ? [rows[i][0], rows[j][0]] : [rows[j][0], rows[i][0]];
1017
+ const key = `${a}|${b}`;
1018
+ const prev = acc.get(key);
1019
+ if (!prev || prev.w < w) acc.set(key, { a, b, w, via: [] });
1020
+ }
1021
+ }
1022
+ return [...acc.keys()].sort().map((key) => acc.get(key));
1023
+ }
1024
+ function blendPairs(sets) {
1025
+ const acc = /* @__PURE__ */ new Map();
1026
+ for (const { pairs, weight } of sets) {
1027
+ if (weight <= 0 || pairs.length === 0) continue;
1028
+ const max = Math.max(...pairs.map((p) => p.w));
1029
+ for (const p of pairs) {
1030
+ const key = `${p.a}|${p.b}`;
1031
+ const hit = acc.get(key) ?? { a: p.a, b: p.b, w: 0, via: [] };
1032
+ hit.w += p.w / max * weight;
1033
+ for (const v of p.via) if (hit.via.length < 6 && !hit.via.includes(v)) hit.via.push(v);
1034
+ acc.set(key, hit);
1035
+ }
1036
+ }
1037
+ return [...acc.keys()].sort().map((key) => acc.get(key));
1038
+ }
1039
+ function lensSnapshots(arrivals, pairs, buckets) {
1040
+ const ids = [...arrivals.keys()].sort();
1041
+ return buckets.map((bucket) => {
1042
+ const t = bucket.end - 1;
1043
+ const nodes = ids.filter((id) => arrivals.get(id) <= t);
1044
+ const active = new Set(nodes);
1045
+ const edges = pairs.filter((p) => active.has(p.a) && active.has(p.b)).map((p) => ({ a: p.a, b: p.b, weight: p.w }));
1046
+ const activity = new Map(nodes.map((id) => [id, arrivals.get(id)]));
1047
+ return { t, nodes, edges, activity };
1048
+ });
1049
+ }
1050
+
1051
+ // src/temporal/bridges.ts
1052
+ function bridgingScores(edges, labels) {
1053
+ const cross = /* @__PURE__ */ new Map();
1054
+ const foreign = /* @__PURE__ */ new Map();
1055
+ const touch = (id, otherLabel, w) => {
1056
+ cross.set(id, (cross.get(id) ?? 0) + w);
1057
+ (foreign.get(id) ?? foreign.set(id, /* @__PURE__ */ new Set()).get(id)).add(otherLabel);
1058
+ };
1059
+ for (const e of edges) {
1060
+ const la = labels.get(e.a);
1061
+ const lb = labels.get(e.b);
1062
+ if (la === void 0 || lb === void 0 || la === lb) continue;
1063
+ const w = e.weight ?? 1;
1064
+ touch(e.a, lb, w);
1065
+ touch(e.b, la, w);
1066
+ }
1067
+ let max = 0;
1068
+ const raw = /* @__PURE__ */ new Map();
1069
+ for (const [id, w] of cross) {
1070
+ const r = w * foreign.get(id).size;
1071
+ raw.set(id, r);
1072
+ if (r > max) max = r;
1073
+ }
1074
+ const out = /* @__PURE__ */ new Map();
1075
+ for (const [id] of labels) {
1076
+ const r = raw.get(id) ?? 0;
1077
+ const own = labels.get(id);
1078
+ const groups = r > 0 ? [own, ...[...foreign.get(id)].sort()] : [own];
1079
+ out.set(id, { score: max > 0 ? r / max : 0, groups });
1080
+ }
1081
+ return out;
1082
+ }
1083
+ function topBridges(bridging, n = 5) {
1084
+ return [...bridging.entries()].filter(([, b]) => b.score > 0).sort((x, y) => y[1].score - x[1].score || (x[0] < y[0] ? -1 : 1)).slice(0, n).map(([id, b]) => ({ id, score: Math.round(b.score * 100) / 100, groups: b.groups }));
1085
+ }
881
1086
  //# sourceMappingURL=index.cjs.map