Skip to content

Architecture

Echo has two AXMTools over a shared extraction and embedding pipeline. Structural statement comparison is a separate library capability; it does not verify the candidates returned by either tool.

graph TD
    Scope["scope · configuration → workspace roots"] --> Corpus["corpus · axm-ast Python extraction"]
    Corpus --> Code["echo_code · documented non-accessors"]
    Corpus --> Check["echo_check · all documented symbols"]
    Code --> Embedding["embedding · TF-IDF or MiniLM"]
    Check --> Embedding
    Embedding --> Clusters["cross-package pairs → heuristics → components"]
    Embedding --> Ranking["intention → exact cosine top-k"]
    Clusters --> Waivers["first-root acknowledgements → bounded report"]
    Ranking --> Candidates["location tags → ranked candidates"]
    Structural["structural · normalized Python statement sets"]

Modules

Module Responsibility
tools Orchestration, validation and ToolResult/text rendering.
corpus Discover packages and extract functions/classes via axm-ast.
scope Read echo.workspace_roots through axm-config.
embedding TF-IDF or MiniLM embeddings and exact neighbour search.
cluster Cross-package pairs, heuristic demotion and union-find components.
waiver Hash identities, mark acknowledgements and report stale entries.
structural Normalized statement shapes and Jaccard similarity.

What similarity measures

Both tools embed signatures plus docstrings. They exclude undocumented symbols, even though the library corpus extractor returns them with a signature fallback. No function bodies are embedded by the corpus pipeline. Extraction is Python-only; axm-ast's broader language support does not make echo a multi-language scanner. It prefers src/ when present. Its excluded path segments include literal tests, .venv, venv, site-packages, __pycache__, node_modules, .tox, build, dist and .git. A flat package's tests_axm_* directory is not covered by that literal tests exclusion, so test helpers can enter the corpus.

echo_code drops accessor-shaped promises, then finds pairs with distinct package names. Pairs whose two sides have boilerplate promises are demoted; package-prefixed names can be demoted as parallel APIs. First-line frequency and short docstrings are heuristics, not proof of boilerplate. A real duplicate can be filtered out.

Surviving edges form connected components. The tool ranks components by their maximum edge score and discards components above max_cluster_size. This does not prove that every pair within a component is similar. Same-package duplication is outside this tool's search.

echo_check retains documented accessors and embeds the intention together with the corpus. Its location verdict and docstring-length promotion flag help triage; they do not establish a valid import contract. An empty search does not demonstrate that the behaviour is absent.

Backend tradeoffs and effects

Surface Default
EchoCodeTool, EchoCheckTool / CLI / MCP st
embed(texts) tfidf

TF-IDF fits a new vocabulary on each call and returns a dense NumPy matrix. Embed query and corpus in the same call: separately fitted matrices do not share a feature space. It never loads torch. Empty text input or an empty vocabulary can raise a vectorizer error.

MiniLM uses all-MiniLM-L6-v2 through sentence-transformers in-process. Loading may fetch model files over the network and write its cache. The model is memoized for subsequent calls within a process; it attempts MPS placement when available. Its loader sets TOKENIZERS_PARALLELISM=false only if unset. Backend failures do not automatically switch to TF-IDF; the caller must retry explicitly.

Reading the corpus does not execute scanned Python code or modify it. Configuration resolution reads axm-config's store, and neural initialization has the cache/environment effects above. Use explicit scope and TF-IDF for a local scan without a model download.

Scale and interpretation

Exact cosine search has no ANN index. echo_code computes pair blocks against the full matrix, so pair generation remains quadratic in corpus size. Dense TF-IDF storage can also be large. Output caps do not cap that work. Narrow the scope before a large scan.

Package identity comes from the directory name, not project metadata. Two checkouts with the same package directory name are treated as the same package for pair exclusion; waiver identities also omit paths and workspace names. Avoid overlapping versions of a package in a scope when interpreting cross-package results.

Module-level public symbols are not necessarily exported from their package root. Unreadable/unparseable files can be skipped, filters may suppress valid reuse candidates, and textual similarity may reward inaccurate docstrings. Read the implementation before changing dependencies or removing code.