Generators
Info
Architectural truth for
Generatorsin theDocumentsfeature. This leaf module acts as the API-side bridge for the asynchronous document generation pipeline, managing data gathering and persistence via CQRS messaging.
Overview
The Documents/Generators module does not execute LLM interactions or long-running computations. Instead, it owns the API side of the generation messaging contracts (coordinated via Wolverine over RabbitMQ) and the handlers that interact with milton-db (via EF Core) and S3 (via the Claim-Check pattern).
The actual computation runs in the MILTON.DocumentGenerator worker service.
Business Logic Intent
Document generation is separated into two logical halves:
- API (State & Storage): Owns the SQL database. It gathers context for generation (files, project presets), packages it into a claim-check, and later receives the result to save back to the database.
- Worker (Pure Functions): Receives a claim-check key, executes LLM prompts or Python API calls, and writes the output back to a claim-check. It never touches
milton-db.
The Generators folder holds the API half of this contract, ensuring that the worker service operates statelessly.
The Claim-Check Pattern
Message brokers (RabbitMQ) struggle with large payloads (source files, generated documents). Therefore, this feature heavily uses the Claim-Check Pattern:
- The API gathers large context data (e.g., source code, LLM presets).
- It uploads this to an
IClaimCheckStore(S3) and gets astringkey. - It sends a lightweight message (e.g.,
TestCaseGenerationRequested) over RabbitMQ containing just theGuid SagaId,Guid BlockId, and theClaimCheckKey. - The worker reads the S3 object, processes it, writes the result to S3, and replies with a
TestCaseGenerationCompletedcontaining a newResultClaimCheckKey.
Handlers & Flow
For every block type (e.g., Requirement, Scanner, TestCase), there are two handlers in this module:
Prepare[Type]GenerationHandler: Triggered by a command. Gathers database context, creates the claim-check payload, and fires the[Type]GenerationRequestedmessage.Apply[Type]GenerationHandler: Triggered when the worker finishes. Reads the result claim-check, updatesmilton-dbwith the new block content, and fires aBlockProcessedEvent.
Sequence: The Generation Round-Trip
sequenceDiagram participant API as API (milton-db) participant S3 as S3 (Claim-Check) participant Bus as Wolverine participant Worker as DocGen Worker note over API: Prepare Phase API->>API: Load Block & Presets from DB API->>S3: PutAsync(Payload) S3-->>API: returns InputKey API->>Bus: Publish(TypeGenerationRequested, InputKey) note over Worker: Execution Phase Bus->>Worker: Consume(TypeGenerationRequested) Worker->>S3: GetAsync(InputKey) Worker->>Worker: Run LLM / Processor Worker->>S3: PutAsync(Result) S3-->>Worker: returns ResultKey Worker->>Bus: Publish(TypeGenerationCompleted, ResultKey) note over API: Apply Phase Bus->>API: Consume(TypeGenerationCompleted) API->>S3: GetAsync(ResultKey) API->>API: Update milton-db (EF Core) API->>Bus: Publish(BlockProcessedEvent)
Messaging Contracts
This module defines API-side mirrors of the message contracts. To enable cross-service routing without a shared assembly, the [MessageIdentity("...")] attributes strictly match the worker’s attributes.
- Commands (Triggered locally):
PrepareRequirementGenerationCommand,PrepareScannerGenerationCommand,PrepareTestCaseGenerationCommand - Outgoing Events (Sent to Worker):
RequirementGenerationRequested,ScannerGenerationRequested - Incoming Events (Received from Worker):
RequirementGenerationCompleted,ScannerGenerationCompleted - Orchestration Events:
BlockProcessedEvent,DocumentCreatedEvent,GeneratePdfCommand