O Aider implementa um sistema sofisticado de análise de código que combina análise estática com modelos de linguagem. Ele utiliza a biblioteca tree-sitter através do módulo grep_ast para realizar análise sintática do código. Em aider/repomap.py, podemos ver a implementação:
from grep_ast import TreeContext, filename_to_lang
from grep_ast.tsl import USING_TSL_PACK, get_language, get_parser
class RepoMap:
def get_tree_context(self, fname, code=None):
if not code:
code = self.io.read_text(fname)
if not code:
return
try:
context = TreeContext(
fname,
code,
color=False,
line_number=True,
child_context=False,
last_line=False,
margin=0,
mark_lois=True,
loi_pad=3,
show_top_of_file_parent_scope=False,
)
return context
except ValueError:
returnA análise sintática permite ao Aider:
- Extrair estruturas do código
- Identificar funções e classes
- Mapear dependências
- Localizar pontos de edição precisamente
O Aider usa o LiteLLM como abstração para interagir com diferentes modelos de linguagem. Em aider/llm.py:
class LazyLiteLLM:
def _load_litellm(self):
self._lazy_module = importlib.import_module("litellm")
self._lazy_module.suppress_debug_info = True
self._lazy_module.set_verbose = False
self._lazy_module.drop_params = True-
Análise Inicial:
- O código é parseado usando tree-sitter
- Uma AST (Abstract Syntax Tree) é gerada
- Estruturas importantes são identificadas
-
Contextualização para o LLM:
- A AST é convertida em um formato que o LLM pode entender
- Informações relevantes são extraídas do mapa do repositório
- O contexto é formatado apropriadamente
-
Processamento pelo LLM:
- O modelo recebe o contexto estruturado
- Analisa as mudanças necessárias
- Propõe edições precisas
O Aider implementa um sistema de linting em aider/linter.py que trabalha em conjunto com a análise estática:
class Linter:
def __init__(self, encoding="utf-8", root=None):
self.languages = dict(
python=self.py_lint,
)
def find_syntax_errors(self, fname, code):
try:
lang = filename_to_lang(fname)
if not lang:
return
parser = get_parser(lang)
if not parser:
return
except Exception:
return
tree = parser.parse(code.encode())
errors = traverse_tree(tree.root_node)
return errorsO Aider mantém um mapa detalhado do repositório que ajuda na análise de código. Em aider/repomap.py:
def get_ranked_tags_map(self, chat_fnames, other_fnames=None):
if not chat_fnames:
return ""
tags = self.get_tags(chat_fnames, other_fnames)
if not tags:
return ""
return self.format_tags_map(tags)Este mapa inclui:
- Estrutura de arquivos
- Definições de funções e classes
- Dependências entre arquivos
- Metadados do repositório
Quando o LLM sugere mudanças, o Aider usa diferentes formatos de edição:
- Unified Diff: Em
aider/coders/udiff_coder.py
class UnifiedDiffCoder(Coder):
def get_edits(self):
content = self.partial_response_content
raw_edits = list(find_diffs(content))- Search and Replace: Em
aider/coders/search_replace.py
def search_and_replace(content, original, updated):
dmp = diff_match_patch()
patches = dmp.patch_make(original, updated)
new_content, results = dmp.patch_apply(patches, content)O Aider implementa várias camadas de validação:
- Sintática: Verifica se as mudanças mantêm o código sintaticamente válido
- Contextual: Assegura que as edições fazem sentido no contexto
- Git: Integra com git para tracking de mudanças
É simplesmente incrível!!!