Skip to content

codenib.integrations.locagent

LocAgent/OpenHands-compatible tools over CodeNib repository views.

Classes:

Name Description
LocAgentToolProvider

Serve LocAgent's three repository tools from one ServerContext.

Functions:

Name Description
get_locagent_tool_schemas

Return dependency-free OpenAI schemas for the pinned LocAgent tools.

bind_locagent_tools

Return the three functions expected by an OpenHands runtime plugin.

dispatch_locagent_tool_call

Dispatch a pinned LocAgent/OpenHands tool call without code evaluation.

LocAgentToolProvider

LocAgentToolProvider(
    context: Any,
    *,
    max_results: int = 5,
    context_lines: int = 20,
    max_source_lines: int = 220,
    max_output_chars: int = 48000,
    traversal_max_nodes: int = 80
)

Serve LocAgent's three repository tools from one ServerContext.

Methods:

Name Description
bindings

Return functions suitable for an OpenHands-style runtime export.

dispatch

Execute a function call directly, without IPython string evaluation.

search_code_snippets

Search entities, indexed code, or 1-based lines.

get_entity_contents

Return deterministic source for exact or disambiguated entities.

explore_tree_structure

Traverse CodeNib graph facts under LocAgent relation names.

explore_graph_structure

Compatibility wrapper for LocAgent's non-function-calling path.

Source code in codenib/integrations/locagent.py
def __init__(
    self,
    context: Any,
    *,
    max_results: int = 5,
    context_lines: int = 20,
    max_source_lines: int = 220,
    max_output_chars: int = 48_000,
    traversal_max_nodes: int = 80,
) -> None:
    self.repository = RepositoryAdapter(context, require_graph=True)
    self.max_results = max(1, int(max_results))
    self.context_lines = max(0, int(context_lines))
    self.max_source_lines = max(20, int(max_source_lines))
    self.max_output_chars = max(2_000, int(max_output_chars))
    self.traversal_max_nodes = max(2, int(traversal_max_nodes))

bindings

bindings() -> dict[str, Callable[..., str]]

Return functions suitable for an OpenHands-style runtime export.

Source code in codenib/integrations/locagent.py
def bindings(self) -> dict[str, Callable[..., str]]:
    """Return functions suitable for an OpenHands-style runtime export."""

    return {
        "search_code_snippets": self.search_code_snippets,
        "get_entity_contents": self.get_entity_contents,
        "explore_tree_structure": self.explore_tree_structure,
    }

dispatch

dispatch(name: str, arguments: Mapping[str, Any] | str | None = None) -> str

Execute a function call directly, without IPython string evaluation.

Source code in codenib/integrations/locagent.py
def dispatch(
    self, name: str, arguments: Mapping[str, Any] | str | None = None
) -> str:
    """Execute a function call directly, without IPython string evaluation."""

    if isinstance(arguments, str):
        try:
            decoded = json.loads(arguments)
        except json.JSONDecodeError as exc:
            raise ValueError(
                "LocAgent tool arguments must be a valid JSON object"
            ) from exc
        if not isinstance(decoded, Mapping):
            raise ValueError("LocAgent tool arguments must decode to a JSON object")
        parsed = dict(decoded)
    else:
        parsed = dict(arguments or {})
    function = self.bindings().get(name)
    if function is None:
        supported = ", ".join(_TOOL_NAMES)
        raise KeyError(f"unknown LocAgent tool {name!r}; supported: {supported}")
    return function(**parsed)

search_code_snippets

search_code_snippets(
    search_terms: Sequence[str] | None = None,
    line_nums: Sequence[int] | int | None = None,
    file_path_or_pattern: str | None = "**/*.py",
) -> str

Search entities, indexed code, or 1-based lines.

Source code in codenib/integrations/locagent.py
def search_code_snippets(
    self,
    search_terms: Sequence[str] | None = None,
    line_nums: Sequence[int] | int | None = None,
    file_path_or_pattern: str | None = "**/*.py",
) -> str:
    """Search entities, indexed code, or 1-based lines."""

    terms = self._coerce_strings(search_terms)
    lines = [line_nums] if isinstance(line_nums, int) else list(line_nums or ())
    if not terms and not lines:
        return (
            "No search requested. Provide at least one search term or 1-based "
            "line number."
        )

    matched_files = self.repository.match_files(file_path_or_pattern)
    prefix = ""
    if file_path_or_pattern and not matched_files:
        matched_files = list(self.repository.files)
        prefix = (
            f"No files matched pattern {file_path_or_pattern!r}; "
            "searching all indexed files.\n\n"
        )
    file_filter = set(matched_files)

    sections: list[str] = []
    for term in terms:
        sections.append(self._search_term(term, file_filter))
    if lines:
        sections.append(
            self._search_lines(lines, file_path_or_pattern, matched_files)
        )
    return self._bounded(
        prefix + "\n\n".join(section for section in sections if section)
    )

get_entity_contents

get_entity_contents(entity_names: Sequence[str]) -> str

Return deterministic source for exact or disambiguated entities.

Source code in codenib/integrations/locagent.py
def get_entity_contents(self, entity_names: Sequence[str]) -> str:
    """Return deterministic source for exact or disambiguated entities."""

    sections: list[str] = []
    for raw_name in self._coerce_strings(entity_names):
        heading = (
            f"## Searching for entity `{raw_name}`...\n" "### Search Result:\n"
        )
        files = self.repository.find_files(raw_name)
        entities = self.repository.find_entities(raw_name)
        if len(files) == 1:
            file_entity = self.repository.entity_for_file(files[0])
            if file_entity is not None:
                sections.append(
                    heading + self._format_entity(file_entity, complete=True)
                )
                continue
        if len(entities) == 1:
            sections.append(
                heading + self._format_entity(entities[0], complete=True)
            )
        elif len(entities) > 1:
            sections.append(
                heading + self._format_disambiguation(raw_name, entities)
            )
        else:
            sections.append(
                heading
                + "Invalid name.\n"
                + 'Hint: use "file_path:QualifiedName" or a repository-relative file.'
            )
    if not sections:
        return "No entity names were provided."
    return self._bounded("\n\n".join(sections))

explore_tree_structure

explore_tree_structure(
    start_entities: Sequence[str],
    direction: str = "downstream",
    traversal_depth: int = 2,
    entity_type_filter: Sequence[str] | None = None,
    dependency_type_filter: Sequence[str] | None = None,
) -> str

Traverse CodeNib graph facts under LocAgent relation names.

Source code in codenib/integrations/locagent.py
def explore_tree_structure(
    self,
    start_entities: Sequence[str],
    direction: str = "downstream",
    traversal_depth: int = 2,
    entity_type_filter: Sequence[str] | None = None,
    dependency_type_filter: Sequence[str] | None = None,
) -> str:
    """Traverse CodeNib graph facts under LocAgent relation names."""

    if direction not in {"upstream", "downstream", "both"}:
        raise ValueError(
            "direction must be one of 'upstream', 'downstream', or 'both'"
        )
    depth = int(traversal_depth)
    if depth < -1:
        raise ValueError("traversal_depth must be -1 or non-negative")

    entity_kinds: set[str] = set()
    for entity_type in entity_type_filter or ():
        if entity_type not in _ENTITY_TYPES:
            supported = ", ".join(sorted(_ENTITY_TYPES))
            raise ValueError(
                f"unknown entity type {entity_type!r}; supported: {supported}"
            )
        entity_kinds.update(_ENTITY_TYPES[entity_type])

    requested_relations = list(dependency_type_filter or _RELATION_TO_EDGE)
    invalid_relations = sorted(set(requested_relations) - set(_RELATION_TO_EDGE))
    if invalid_relations:
        supported = ", ".join(sorted(_RELATION_TO_EDGE))
        raise ValueError(
            f"unknown dependency types {invalid_relations}; supported: {supported}"
        )
    edge_types = {_RELATION_TO_EDGE[name] for name in requested_relations}

    sections: list[str] = []
    broader_used: set[str] = set()
    for raw_start in self._coerce_strings(start_entities):
        if raw_start == "/":
            sections.append(self.repository.file_tree(max_depth=depth))
            continue
        candidates = self.repository.find_entities(raw_start)
        if len(candidates) != 1:
            if candidates:
                sections.append(self._format_disambiguation(raw_start, candidates))
            else:
                sections.append(f"Invalid start entity `{raw_start}`.")
            continue
        start = candidates[0]
        rows = self.repository.traverse(
            start,
            direction=direction,
            depth=depth,
            edge_types=edge_types,
            entity_kinds=entity_kinds,
            max_nodes=self.traversal_max_nodes,
        )
        rendered = [f"{start.locagent_id} [{self._external_kind(start.kind)}]"]
        for level, relation, neighbor in rows:
            relation_name = _EDGE_TO_RELATION.get(
                relation.edge_type, relation.edge_type
            )
            if relation_name in _BROADER_RELATIONS:
                broader_used.add(relation_name)
            if relation.source.vertex_id == neighbor.vertex_id:
                relation_text = f"<- {relation_name} -"
            else:
                relation_text = f"{relation_name} ->"
            rendered.append(
                f"{'  ' * level}- {relation_text} "
                f"{neighbor.locagent_id} [{self._external_kind(neighbor.kind)}]"
            )
        if not rows:
            rendered.append("  (no matching relations)")
        sections.append("\n".join(rendered))

    if broader_used:
        notes = "; ".join(
            f"{name}: {_BROADER_RELATIONS[name]}" for name in sorted(broader_used)
        )
        sections.append(f"Relation fidelity: {notes}.")
    if not sections:
        return "No start entities were provided."
    return self._bounded("\n\n".join(sections))

explore_graph_structure

explore_graph_structure(
    start_entities: Sequence[str],
    direction: str = "downstream",
    traversal_depth: int = 1,
    entity_type_filter: Sequence[str] | None = None,
    dependency_type_filter: Sequence[str] | None = None,
) -> str

Compatibility wrapper for LocAgent's non-function-calling path.

Source code in codenib/integrations/locagent.py
def explore_graph_structure(
    self,
    start_entities: Sequence[str],
    direction: str = "downstream",
    traversal_depth: int = 1,
    entity_type_filter: Sequence[str] | None = None,
    dependency_type_filter: Sequence[str] | None = None,
) -> str:
    """Compatibility wrapper for LocAgent's non-function-calling path."""

    return self.explore_tree_structure(
        start_entities=start_entities,
        direction=direction,
        traversal_depth=traversal_depth,
        entity_type_filter=entity_type_filter,
        dependency_type_filter=dependency_type_filter,
    )

get_locagent_tool_schemas

get_locagent_tool_schemas() -> list[dict[str, Any]]

Return dependency-free OpenAI schemas for the pinned LocAgent tools.

Source code in codenib/integrations/locagent.py
def get_locagent_tool_schemas() -> list[dict[str, Any]]:
    """Return dependency-free OpenAI schemas for the pinned LocAgent tools."""

    return copy.deepcopy(list(_LOCAGENT_TOOL_SCHEMAS))

bind_locagent_tools

bind_locagent_tools(provider: LocAgentToolProvider) -> dict[str, Callable[..., str]]

Return the three functions expected by an OpenHands runtime plugin.

Source code in codenib/integrations/locagent.py
def bind_locagent_tools(
    provider: LocAgentToolProvider,
) -> dict[str, Callable[..., str]]:
    """Return the three functions expected by an OpenHands runtime plugin."""

    return provider.bindings()

dispatch_locagent_tool_call

dispatch_locagent_tool_call(
    provider: LocAgentToolProvider,
    name: str,
    arguments: Mapping[str, Any] | str | None = None,
) -> str

Dispatch a pinned LocAgent/OpenHands tool call without code evaluation.

Source code in codenib/integrations/locagent.py
def dispatch_locagent_tool_call(
    provider: LocAgentToolProvider,
    name: str,
    arguments: Mapping[str, Any] | str | None = None,
) -> str:
    """Dispatch a pinned LocAgent/OpenHands tool call without code evaluation."""

    return provider.dispatch(name, arguments)