import {
  listEntitiesForMemory,
  listMemoryIdsForEntity,
  neighborsOf
} from "./chunk-Z6RT5BUO.js";
import {
  getMemory
} from "./chunk-UBPGOOAN.js";
import {
  hybridSearch,
  searchMemoryFtsIndex
} from "./chunk-JRIM7VBY.js";

// src/graph/retrieval.ts
var SEED_WEIGHT = 1;
var GRAPH_WEIGHT = 0.6;
var ENTITY_OVERLAP_BONUS = 0.15;
var HOP_DECAY = 0.55;
async function graphQuery(db, query, embeddingConfig, options = {}) {
  const seedLimit = options.seedLimit ?? 10;
  const hops = Math.max(0, Math.min(4, options.hops ?? 2));
  const limit = options.limit ?? 15;
  let seedHits;
  if (embeddingConfig) {
    const hybrid = await hybridSearch(db, query, embeddingConfig, {
      repo: options.repo,
      limit: seedLimit
    });
    seedHits = hybrid.map((h) => ({ memory: h.memory, score: h.score }));
  } else {
    const fts = searchMemoryFtsIndex(db, query, {
      repo: options.repo,
      limit: seedLimit
    });
    seedHits = fts.map((row, i) => {
      const mem = getMemory(db, row.memory_id);
      if (!mem) return null;
      const score = 1 / (1 + Math.abs(row.lexical_rank) + i);
      return { memory: mem, score };
    }).filter((x) => x !== null);
  }
  const entityMap = /* @__PURE__ */ new Map();
  const seedEntityIds = /* @__PURE__ */ new Set();
  for (const seed of seedHits) {
    const ents = listEntitiesForMemory(db, seed.memory.id);
    for (const e of ents) {
      entityMap.set(e.id, e);
      seedEntityIds.add(e.id);
    }
  }
  const expandedEntityIds = new Set(seedEntityIds);
  const entityHopDistance = /* @__PURE__ */ new Map();
  for (const id of seedEntityIds) entityHopDistance.set(id, 0);
  if (hops > 0 && seedEntityIds.size > 0) {
    for (const seedId of seedEntityIds) {
      const walk = neighborsOf(db, seedId, {
        hops,
        relationTypes: options.relationTypes
      });
      for (const ent of walk.entities) {
        entityMap.set(ent.id, ent);
        if (!expandedEntityIds.has(ent.id)) {
          expandedEntityIds.add(ent.id);
          entityHopDistance.set(ent.id, 1);
        }
      }
    }
  }
  const hits = /* @__PURE__ */ new Map();
  for (const seed of seedHits) {
    const ents = listEntitiesForMemory(db, seed.memory.id);
    hits.set(seed.memory.id, {
      memory: seed.memory,
      via: "seed",
      score: seed.score * SEED_WEIGHT,
      hops: 0,
      shared_entities: ents
    });
  }
  if (hops > 0) {
    for (const entId of expandedEntityIds) {
      const hop = entityHopDistance.get(entId) ?? 1;
      const ent = entityMap.get(entId);
      if (!ent) continue;
      const decay = Math.pow(HOP_DECAY, hop);
      const memIds = listMemoryIdsForEntity(db, entId);
      for (const memId of memIds) {
        const existing = hits.get(memId);
        if (existing) {
          if (!existing.shared_entities.find((e) => e.id === entId)) {
            existing.shared_entities.push(ent);
            existing.score += ENTITY_OVERLAP_BONUS * decay;
          }
          continue;
        }
        const mem = getMemory(db, memId);
        if (!mem) continue;
        if (options.repo && mem.repo && mem.repo !== options.repo) continue;
        hits.set(memId, {
          memory: mem,
          via: "graph",
          score: GRAPH_WEIGHT * decay,
          hops: hop,
          shared_entities: [ent]
        });
      }
    }
  }
  const sorted = Array.from(hits.values()).sort((a, b) => b.score - a.score).slice(0, limit);
  const entitiesOut = {};
  for (const [id, row] of entityMap) entitiesOut[id] = row;
  return {
    hits: sorted,
    seed_count: seedHits.length,
    expanded_entities: expandedEntityIds.size,
    entities: entitiesOut
  };
}

export {
  graphQuery
};
//# sourceMappingURL=chunk-AKODTKWM.js.map