# Pontos de Integração — Módulo AI com Sistema Existente

## Visão Geral

O módulo `ai/` não existe isoladamente — ele se integra profundamente com os módulos existentes (`workspace`, `analysis`, `job`) através de eventos de domínio, dependências Modulith e jobs assíncronos.

Este documento descreve cada ponto de integração, o fluxo de dados e as modificações necessárias nos módulos existentes.

---

## 1. Diagrama de Sequência Completo

```
Usuário         Controller    AnalysisSvc    JobModule       AI Module      Ollama/LLM
  │                 │              │              │               │              │
  │ POST /upload    │              │              │               │              │
  ├────────────────►│              │              │               │              │
  │                 │  save        │              │               │              │
  │                 ├─────────────►│              │               │              │
  │                 │              │ BinaryUp-    │               │              │
  │                 │              │ loadedEvent  │               │              │
  │                 │              ├─────────────►│               │              │
  │                 │              │              │ ANALYZE_FILE  │              │
  │                 │              │              │ (DiE Docker)  │              │
  │                 │              │              │               │              │
  │ POST /analyze   │              │              │               │              │
  ├────────────────►│              │              │               │              │
  │                 │              │ StaticAnlys  │               │              │
  │                 │              │ RequestedEvt │               │              │
  │                 │              ├─────────────►│               │              │
  │                 │              │              │ STATIC_ANALYS │              │
  │                 │              │              │ (IDA5 Docker) │              │
  │                 │              │◄─────────────┤ saveResult()  │              │
  │                 │              │              │               │              │
  │                 │              │ StaticAnlys  │               │              │
  │                 │              │ CompletedEvt │               │              │
  │                 │              ├─────────────►│               │              │
  │                 │              │              │ GENERATE_EMB  │              │
  │                 │              │              ├──────────────►│              │
  │                 │              │              │               │ indexBinary() │
  │                 │              │              │               ├──────────────►│
  │                 │              │              │               │◄──────────────┤
  │                 │              │              │◄──────────────┤  embeddings   │
  │                 │              │              │               │               │
  │                 │              │              │  (binário indexado)           │
  │                 │              │              │               │               │
  │ POST /chat/...  │              │              │               │               │
  ├────────────────►│              │              │               │               │
  │                 │              │              │               │ chat()        │
  │                 │              │              │               ├──────────────►│
  │                 │◄─────────────┼──────────────┼───────────────┤ SSE stream    │
  │◄────────────────┤              │              │               │               │
```

## 2. Eventos

### 2.1 Novo Evento: `StaticAnalysisCompletedEvent`

**Definição**: `analysis/event/StaticAnalysisCompletedEvent.java`

```java
package ai.decompile.analysis.event;

import java.util.UUID;

/**
 * Published when a static analysis job completes successfully.
 * Triggers the embedding generation pipeline.
 */
public record StaticAnalysisCompletedEvent(
    UUID binaryId,
    UUID projectId,
    UUID workspaceId,
    UUID analysisId,
    String engine
) {}
```

**Quem publica**: `StaticAnalysisHandler.process()` no módulo `job`

**Quem consome**: `EmbeddingEventSubscriber` no módulo `job`

**Momento da publicação**: Após `analysisService.saveResult()` e `binaryService.markStaticAnalysisDone()` concluírem com sucesso.

### 2.2 Publicação no `StaticAnalysisHandler`

**Arquivo a modificar**: `job/service/handler/StaticAnalysisHandler.java`

**Mudança**: Adicionar publicação do evento após salvar resultados:

```java
// Dentro de process(), após saveResult() e markStaticAnalysisDone():
@Autowired
private ApplicationEventPublisher eventPublisher;

// ... no final de process():
var binary = binaryService.getBinary(job.getBinaryId());
eventPublisher.publishEvent(
    new StaticAnalysisCompletedEvent(
        binary.getId(),
        binary.getProject().getId(),
        binary.getProject().getWorkspace().getId(),
        analysisResponse.id(),
        engineName
    )
);
```

> **Alternativa com Spring Modulith**: Usar `@ApplicationModuleListener` exige que o evento seja publicado via `ApplicationEventPublisher` do Spring. O Spring Modulith intercepta e persiste na tabela `event_publication` para garantia de entrega.

### 2.3 Novo Subscriber: `EmbeddingEventSubscriber`

**Arquivo a criar**: `job/service/EmbeddingEventSubscriber.java`

```java
package ai.decompile.job.service;

import ai.decompile.analysis.event.StaticAnalysisCompletedEvent;
import ai.decompile.job.messaging.JobMessagePublisher;
import ai.decompile.job.model.entity.Job;
import ai.decompile.job.model.enums.JobStatus;
import ai.decompile.job.model.enums.JobType;
import ai.decompile.job.model.repository.JobRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.modulith.events.ApplicationModuleListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Log4j2
@Component
@RequiredArgsConstructor
public class EmbeddingEventSubscriber {

    private final JobRepository jobRepository;
    private final JobMessagePublisher jobMessagePublisher;

    @ApplicationModuleListener
    public void on(StaticAnalysisCompletedEvent event) {
        log.info(">>> EmbeddingEventSubscriber: Received StaticAnalysisCompletedEvent binary={}", event.binaryId());

        var job = createJob(event);
        log.info(">>> EmbeddingEventSubscriber: Created GENERATE_EMBEDDINGS job {} for binary {}", job.getId(), event.binaryId());
        jobMessagePublisher.publish(job.getId());
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    Job createJob(StaticAnalysisCompletedEvent event) {
        return jobRepository.save(
            Job.builder()
                .type(JobType.GENERATE_EMBEDDINGS)
                .status(JobStatus.ENQUEUED)
                .projectId(event.projectId())
                .binaryId(event.binaryId())
                .payload("{\"analysisId\":\"" + event.analysisId() + "\"}")
                .retryCount(0)
                .build()
        );
    }
}
```

## 3. Jobs

### 3.1 Novo `JobType`

**Arquivo a modificar**: `job/model/enums/JobType.java`

```java
public enum JobType {
    ANALYZE_FILE,
    STATIC_ANALYSIS,
    GENERATE_EMBEDDINGS   // <-- novo
}
```

### 3.2 Novo Handler: `EmbeddingGenerationHandler`

**Arquivo a criar**: `job/service/handler/EmbeddingGenerationHandler.java`

```java
package ai.decompile.job.service.handler;

import ai.decompile.ai.service.EmbeddingService;
import ai.decompile.job.model.entity.Job;
import ai.decompile.job.model.enums.JobStatus;
import ai.decompile.job.model.enums.JobType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;

@Log4j2
@Component
public class EmbeddingGenerationHandler implements JobHandler {

    private final EmbeddingService embeddingService;
    private final ObjectMapper objectMapper;

    public EmbeddingGenerationHandler(EmbeddingService embeddingService, ObjectMapper objectMapper) {
        this.embeddingService = embeddingService;
        this.objectMapper = objectMapper;
    }

    @Override
    public void process(Job job) {
        log.info("Processing GENERATE_EMBEDDINGS job {} for binary {}", job.getId(), job.getBinaryId());

        try {
            int chunks = embeddingService.indexBinary(job.getBinaryId());

            job.setResult("{\"chunkCount\":" + chunks + "}");
            job.setStatus(JobStatus.COMPLETED);

            log.info("Job {} completed. Generated {} embedding chunks for binary {}",
                     job.getId(), chunks, job.getBinaryId());
        } catch (Exception e) {
            log.error("Job {} failed: {}", job.getId(), e.getMessage(), e);
            job.setStatus(JobStatus.FAILED);
            job.setErrorMessage(e.getMessage());
        }
    }

    @Override
    public JobType supportedType() {
        return JobType.GENERATE_EMBEDDINGS;
    }
}
```

> **Nota**: `EmbeddingGenerationHandler` não precisa de `@ConditionalOnProperty` como `StaticAnalysisHandler`, pois embeddings não dependem do Docker. Ollama roda no host.

### 3.3 Atualizar `JobHandlerFactory`

Nenhuma mudança necessária — o factory já descobre automaticamente todos os beans `JobHandler` via injeção de lista.

## 4. Dependências Modulith

### 4.1 Atualizar `job/package-info.java`

**Arquivo a modificar**: `job/package-info.java`

```java
@org.springframework.modulith.ApplicationModule(
    displayName = "Job",
    allowedDependencies = {
        "workspace::events",
        "workspace::entities",
        "workspace::services",
        "die::service",
        "die::model",
        "analysis::events",
        "analysis::services",
        "analysis::dto",
        "engine::services",
        "ai::services",        // <-- novo: EmbeddingService
        "ai::model::entity",   // <-- novo: EmbeddingChunk (se necessário)
        "common"
    })
package ai.decompile.job;
```

### 4.2 `analysis/package-info.java`

Não precisa ser alterado. O evento `StaticAnalysisCompletedEvent` é definido no próprio módulo `analysis` (`analysis.event`), então não há nova dependência.

### 4.3 `ai/package-info.java` (novo)

```java
@org.springframework.modulith.ApplicationModule(
    displayName = "AI",
    allowedDependencies = {
        "workspace::entities",       // Binary, Project (para JPA relationships)
        "workspace::services",       // BinaryService (para buscar filePath, metadados)
        "analysis::entities",        // StaticFunction, StaticAnalysis, StaticXref, StaticLabel
        "analysis::services",        // AnalysisService (para buscar funções/xrefs)
        "common"                     // exceções, config
    })
package ai.decompile.ai;
```

## 5. Modificações em Arquivos Existentes

### 5.1 `pom.xml`

Adicionar dependências Spring AI (Spring AI BOM, ollama, openai, pgvector-store).

### 5.2 `application.yml`

Adicionar blocos `spring.ai.*` e `app.ai.*` (ver `configuration.md`).

### 5.3 `job/model/enums/JobType.java`

Adicionar `GENERATE_EMBEDDINGS`.

### 5.4 `job/package-info.java`

Adicionar `ai::services` e `ai::model::entity` aos `allowedDependencies`.

### 5.5 `job/service/handler/StaticAnalysisHandler.java`

Adicionar publicação de `StaticAnalysisCompletedEvent` ao final de `process()`.

### 5.6 `.gitignore`

Adicionar `.env`.

## 6. Arquivos Novos (por módulo)

### Módulo `ai/`

```
src/main/java/ai/decompile/ai/
├── package-info.java
├── config/
│   └── AiConfig.java
├── model/
│   ├── entity/
│   │   ├── package-info.java
│   │   ├── EmbeddingChunk.java
│   │   ├── ChatSession.java
│   │   └── ChatMessage.java
│   ├── repository/
│   │   ├── package-info.java
│   │   ├── EmbeddingChunkRepository.java
│   │   ├── ChatSessionRepository.java
│   │   └── ChatMessageRepository.java
│   └── dto/
│       ├── package-info.java
│       ├── ChatRequest.java
│       ├── SessionResponse.java
│       └── MessageResponse.java
├── service/
│   ├── package-info.java
│   ├── EmbeddingService.java
│   ├── RagService.java
│   └── ChatService.java
└── controller/
    ├── package-info.java
    └── ChatController.java
```

### Módulo `analysis/`

```
src/main/java/ai/decompile/analysis/
└── event/
    └── StaticAnalysisCompletedEvent.java    # novo
```

### Módulo `job/`

```
src/main/java/ai/decompile/job/
├── service/
│   ├── EmbeddingEventSubscriber.java        # novo
│   └── handler/
│       └── EmbeddingGenerationHandler.java   # novo
└── model/enums/
    └── JobType.java                         # modificado (+ GENERATE_EMBEDDINGS)
```

### Database

```
src/main/resources/db/migration/
└── V008__ai_embedding_and_chat.sql          # novo
```

### Raiz do projeto

```
backend/
├── .env                                     # novo
├── .env.example                             # novo (template sem secrets)
└── .gitignore                               # modificado (+ .env)
```

## 7. Ordem de Implementação Recomendada

| Ordem | Tarefa | Depende de |
|---|---|---|
| 1 | `V008__ai_embedding_and_chat.sql` | Nada |
| 2 | Dependências no `pom.xml` | Nada |
| 3 | `application.yml` — blocos AI | Nada |
| 4 | `.env` + `.env.example` + `.gitignore` | Nada |
| 5 | Entidades JPA (`EmbeddingChunk`, `ChatSession`, `ChatMessage`) | V008, pom.xml |
| 6 | Repositórios JPA | Entidades |
| 7 | `AiConfig.java` | pom.xml, application.yml |
| 8 | `EmbeddingService` | Repositórios, AiConfig |
| 9 | `RagService` | EmbeddingService, AnalysisService |
| 10 | `ChatService` | RagService, AiConfig |
| 11 | `ChatController` | ChatService |
| 12 | `StaticAnalysisCompletedEvent` | Nada |
| 13 | `EmbeddingEventSubscriber` | Evento, JobService |
| 14 | `EmbeddingGenerationHandler` | EmbeddingService |
| 15 | `JobType.GENERATE_EMBEDDINGS` | Nada |
| 16 | `job/package-info.java` update | Dependências ai |
| 17 | `StaticAnalysisHandler` — publish event | Evento |
| 18 | `ai/package-info.java` | Dependências workspace, analysis |
| 19 | Testes de integração | Tudo acima |

## 8. Verificação de Integridade

Após implementar todos os pontos de integração, verificar:

```bash
# 1. Compilação limpa
./mvnw clean compile

# 2. Migration roda sem erros
./mvnw flyway:migrate

# 3. Testes de módulo Modulith
./mvnw test -Dtest="*Modulith*"

# 4. Fluxo completo (manual):
#    - Upload de binário
#    - Análise estática
#    - Verificar job GENERATE_EMBEDDINGS criado e concluído
#    - Verificar registros em embedding_chunk
#    - Criar sessão de chat
#    - Enviar mensagem e receber SSE stream
```
