Skip to content

symfonic.memory.backends.in_memory_candidates

in_memory_candidates

Streaming top-k candidate selection for the reference graph backend.

select

select(backend, scope, request)

Scan existing references, retaining at most limit+one profile references.

Source code in src/symfonic/memory/backends/in_memory_candidates.py
def select(backend, scope, request):
    """Scan existing references, retaining at most limit+one profile references."""
    def rows():
        for node in backend._nodes.get(scope.tenant_id, {}).values():
            if not backend._node_visible(scope, node) or not eligible(node, request):
                continue
            if request.exact_scope and stored_scope_path(node.properties, node.tenant_id) != (
                materialise_scope_path(scope)
            ):
                continue
            yield node

    tokens = set(request.cue.lower().split())

    def key(node):
        text = (node.label + " " + str(node.properties.get("content", ""))).lower()
        overlap = len(tokens & set(text.split())) / len(tokens) if tokens else 1.0
        salience = round((min(max(float(node.importance), 1), 10) - 1) / 9, 12)
        prefixes = scope.ancestor_prefix_paths()
        distance = len(prefixes) - 1 - prefixes.index(stored_scope_path(
            node.properties, node.tenant_id))
        score = salience * (0.25 + 0.75 * overlap) * (0.5 ** distance)
        return (-score, distance, str(node.id))

    reserved = nsmallest(request.profile_slots, (node for node in rows() if profile(node)), key=key)
    ids = {str(node.id) for node in reserved}
    count = 0

    def ordinary():
        nonlocal count
        for node in rows():
            count += 1
            if str(node.id) not in ids:
                yield node

    ranked = nsmallest(request.limit, ordinary(), key=key)
    return CandidatePage(
        tuple(reserved + ranked[:request.limit - len(reserved)]), count <= request.limit,
    )