Ports
Interfaces que definem os contratos entre o core e os adapters.
GitlabClient
from typing import Protocol
from core.models import FileDiff, Finding, MrEvent
class GitlabClient(Protocol):
def fetch_diff(self, event: MrEvent) -> list[FileDiff]: ...
def fetch_compare(self, event: MrEvent) -> list[FileDiff]: ...
def post_inline_comment(self, event: MrEvent, finding: Finding, position: dict) -> None: ...
def post_fallback_comment(self, event: MrEvent, body: str) -> None: ...
Métodos
| Método | Descrição | Exceção |
|---|---|---|
fetch_diff(event) | Busca o diff completo do MR (action=open) | GitlabApiError |
fetch_compare(event) | Busca diff incremental (action=update) | GitlabApiError |
post_inline_comment(event, finding, position) | Posta comentário inline na linha | GitlabApiError |
post_fallback_comment(event, body) | Posta comentário global no MR | GitlabApiError |
LlmClient
from typing import Protocol
from core.models import FileDiff, Finding, MrEvent
class LlmClient(Protocol):
def review_file(self, event: MrEvent, file_diff: FileDiff) -> list[Finding]: ...
Métodos
| Método | Descrição | Exceção |
|---|---|---|
review_file(event, file_diff) | Envia diff para o LLM e retorna findings | LlmParseError |
LlmParseError
class LlmParseError(Exception):
"""Raised when an LLM response fails schema or diff-line validation."""
Levantado quando:
- Resposta do LLM não é JSON válido
- JSON não segue o schema esperado
- Algum finding referencia linha que não está no diff
Dispatcher
from typing import Protocol
from core.models import MrEvent
class Dispatcher(Protocol:
def dispatch(self, event: MrEvent) -> None: ...
Métodos
| Método | Descrição |
|---|---|
dispatch(event) | Dispara o processamento assíncrono do MR |
Implementações
| Classe | Ambiente | Mecanismo |
|---|---|---|
BackgroundTaskDispatcher | Container | asyncio.create_task |
LambdaSelfInvokeDispatcher | Lambda | boto3 lambda.invoke(InvocationType="Event") |
ReviewPipeline
from core.models import MrEvent
class ReviewPipeline:
def __init__(
self,
gitlab_client: GitlabClient,
llm_client: LlmClient,
max_file_lines: int = 800,
): ...
def run(self, event: MrEvent) -> None: ...
Comportamento
- Fetch diffs do GitLab (diff ou compare, baseado na action)
- Para cada arquivo:
- Skip se linhas >
max_file_lines - Chama
llm_client.review_file() - Para cada finding, tenta postar inline ou fallback
- Skip se linhas >
- Erros em um arquivo não afetam outros
- Erro no fetch do diff aborta todo o MR