@dreamtree-org/graphify 1.3.0 → 1.4.0

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/cli/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  runPipeline,
14
14
  saveResult,
15
15
  validateRules
16
- } from "../chunk-UBXYMMXJ.js";
16
+ } from "../chunk-QEB7A5KB.js";
17
17
  import {
18
18
  affectedBy,
19
19
  buildContextPack,
@@ -25,7 +25,7 @@ import {
25
25
  shortestPath,
26
26
  testsForChangedFiles,
27
27
  testsForNode
28
- } from "../chunk-K322XB4U.js";
28
+ } from "../chunk-7LTO76UD.js";
29
29
  import {
30
30
  validateDsn,
31
31
  validateUrl
package/dist/index.cjs CHANGED
@@ -44,6 +44,7 @@ __export(index_exports, {
44
44
  collectFiles: () => collectFiles,
45
45
  deserializeGraph: () => deserializeGraph,
46
46
  diffGraphs: () => diffGraphs,
47
+ expandRetrieval: () => expandRetrieval,
47
48
  explainNode: () => explainNode,
48
49
  exportGraph: () => exportGraph,
49
50
  extract: () => extract,
@@ -3200,6 +3201,141 @@ function explainNode(graph, query) {
3200
3201
  };
3201
3202
  }
3202
3203
 
3204
+ // src/retrieval.ts
3205
+ var DEFAULT_TOKEN_BUDGET2 = 2e3;
3206
+ var DEFAULT_MAX_HOPS = 2;
3207
+ function resolveSeeds(graph, hits) {
3208
+ const seeds = /* @__PURE__ */ new Map();
3209
+ for (const hit of hits) {
3210
+ let id = null;
3211
+ if (hit.nodeId !== void 0 && graph.hasNode(hit.nodeId)) {
3212
+ id = hit.nodeId;
3213
+ } else if (hit.text !== void 0) {
3214
+ id = resolveNode(graph, hit.text)?.id ?? null;
3215
+ }
3216
+ const key = id ?? hit.nodeId ?? hit.text ?? "";
3217
+ if (key === "") continue;
3218
+ const existing = seeds.get(key);
3219
+ if (existing) {
3220
+ if (hit.score !== void 0 && (existing.score === void 0 || hit.score > existing.score)) {
3221
+ existing.score = hit.score;
3222
+ }
3223
+ continue;
3224
+ }
3225
+ seeds.set(key, { id: key, score: hit.score, inGraph: id !== null, text: hit.text });
3226
+ }
3227
+ return [...seeds.values()];
3228
+ }
3229
+ function expandRetrieval(graph, hits, options = {}) {
3230
+ const tokenBudget = options.tokenBudget ?? DEFAULT_TOKEN_BUDGET2;
3231
+ const maxHops = options.maxHops ?? DEFAULT_MAX_HOPS;
3232
+ const relations = options.relations ? new Set(options.relations) : null;
3233
+ const seeds = resolveSeeds(graph, hits);
3234
+ const graphSeeds = seeds.filter((s) => s.inGraph);
3235
+ const seedScore = new Map(graphSeeds.map((s) => [s.id, s.score]));
3236
+ const hops = /* @__PURE__ */ new Map();
3237
+ const via = /* @__PURE__ */ new Map();
3238
+ const frontier = [];
3239
+ let spentTokens = 0;
3240
+ for (const seed of graphSeeds) {
3241
+ hops.set(seed.id, 0);
3242
+ frontier.push(seed.id);
3243
+ spentTokens += nodeTokenCost(graph, seed.id);
3244
+ }
3245
+ let truncated = false;
3246
+ while (frontier.length > 0) {
3247
+ const current = frontier.shift();
3248
+ const currentHops = hops.get(current);
3249
+ if (currentHops >= maxHops) continue;
3250
+ const candidates = [];
3251
+ graph.forEachEdge(current, (_key, attrs, source, target) => {
3252
+ const relation = String(attrs.relation);
3253
+ if (relations && !relations.has(relation)) return;
3254
+ const neighbor = source === current ? target : source;
3255
+ candidates.push({
3256
+ neighbor,
3257
+ edge: { source, target, relation, confidence: String(attrs.confidence) }
3258
+ });
3259
+ });
3260
+ candidates.sort((a, b) => a.neighbor.localeCompare(b.neighbor));
3261
+ for (const { neighbor, edge } of candidates) {
3262
+ if (hops.has(neighbor)) continue;
3263
+ const cost = nodeTokenCost(graph, neighbor);
3264
+ if (spentTokens + cost > tokenBudget) {
3265
+ truncated = true;
3266
+ continue;
3267
+ }
3268
+ spentTokens += cost;
3269
+ hops.set(neighbor, currentHops + 1);
3270
+ via.set(neighbor, edge);
3271
+ frontier.push(neighbor);
3272
+ }
3273
+ }
3274
+ const degree = (id) => graph.degree(id);
3275
+ const nodes = [...hops.entries()].map(([id, hopCount]) => {
3276
+ const attrs = graph.getNodeAttributes(id);
3277
+ return {
3278
+ id,
3279
+ label: attrs.label ?? id,
3280
+ ...attrs.kind !== void 0 ? { kind: String(attrs.kind) } : {},
3281
+ sourceFile: attrs.sourceFile ?? "",
3282
+ sourceLocation: attrs.sourceLocation ?? "",
3283
+ hops: hopCount,
3284
+ ...via.has(id) ? { via: via.get(id) } : {},
3285
+ ...hopCount === 0 && seedScore.get(id) !== void 0 ? { seedScore: seedScore.get(id) } : {}
3286
+ };
3287
+ });
3288
+ for (const seed of seeds) {
3289
+ if (seed.inGraph) continue;
3290
+ nodes.push({
3291
+ id: seed.id,
3292
+ label: seed.text ?? seed.id,
3293
+ sourceFile: "",
3294
+ sourceLocation: "",
3295
+ hops: 0,
3296
+ ...seed.score !== void 0 ? { seedScore: seed.score } : {}
3297
+ });
3298
+ }
3299
+ nodes.sort((a, b) => {
3300
+ if (a.hops !== b.hops) return a.hops - b.hops;
3301
+ if (a.hops === 0) {
3302
+ return (b.seedScore ?? -Infinity) - (a.seedScore ?? -Infinity) || a.id.localeCompare(b.id);
3303
+ }
3304
+ const degreeDiff = (graph.hasNode(b.id) ? degree(b.id) : 0) - (graph.hasNode(a.id) ? degree(a.id) : 0);
3305
+ return degreeDiff || a.id.localeCompare(b.id);
3306
+ });
3307
+ const included = /* @__PURE__ */ new Set([...hops.keys()]);
3308
+ const edges = [];
3309
+ graph.forEachEdge((_key, attrs, source, target) => {
3310
+ if (!included.has(source) || !included.has(target)) return;
3311
+ const relation = String(attrs.relation);
3312
+ if (relations && !relations.has(relation)) return;
3313
+ edges.push({ source, target, relation, confidence: String(attrs.confidence) });
3314
+ });
3315
+ edges.sort(
3316
+ (a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target) || a.relation.localeCompare(b.relation)
3317
+ );
3318
+ const communities = /* @__PURE__ */ new Map();
3319
+ for (const id of included) {
3320
+ const attrs = graph.getNodeAttributes(id);
3321
+ const community = attrs.community;
3322
+ if (community === void 0) continue;
3323
+ const entry = communities.get(community) ?? {
3324
+ id: community,
3325
+ label: attrs.communityLabel ?? String(community),
3326
+ seedCount: 0
3327
+ };
3328
+ if (hops.get(id) === 0) entry.seedCount += 1;
3329
+ communities.set(community, entry);
3330
+ }
3331
+ return {
3332
+ nodes,
3333
+ edges,
3334
+ communities: [...communities.values()].sort((a, b) => b.seedCount - a.seedCount || a.id - b.id),
3335
+ truncated
3336
+ };
3337
+ }
3338
+
3203
3339
  // src/impact.ts
3204
3340
  var DEPENDENCY_RELATIONS = /* @__PURE__ */ new Set([
3205
3341
  "calls",
@@ -4005,6 +4141,7 @@ function checkRules(graph, config) {
4005
4141
  collectFiles,
4006
4142
  deserializeGraph,
4007
4143
  diffGraphs,
4144
+ expandRetrieval,
4008
4145
  explainNode,
4009
4146
  exportGraph,
4010
4147
  extract,