Files
LexiChain/docs/02-services.md
2026-05-13 21:08:27 +01:00

13 KiB

02 — Services Reference

All domain services live in lib/services/. They are plain TypeScript classes with static methods — no instantiation needed. Services are server-only (never imported in client components).


Table of Contents

  1. AIService
  2. RAGService
  3. BlockchainService
  4. ContractService
  5. NotificationService
  6. EmailService
  7. StatsService
  8. CertificateService
  9. StorageService

1. AIService

File: lib/services/ai.service.ts
Sub-modules: lib/services/ai/ (parser, normalizer, prompt builder, key manager)

Purpose

Extracts structured information from uploaded contract documents (PDF or image). Implements a multi-model fallback chain with automatic retry and JSON repair.

Pipeline

File URL
  ↓
Download bytes + resolve MIME type
  ↓
Pre-validation (is this actually a contract?)
  ↓
Build adaptive prompt (with context from previous analyses)
  ↓
[Attempt 1..N]
  │
  ├── Try Gemini primary model (key rotation across AI_API_KEY1/2/3)
  ├── Try Gemini secondary model (if AI_MODEL_SECONDARY_GEMINI is set)
  ├── Try Gemini with lenient settings (last Gemini attempt)
  │
  └── If all Gemini models fail → Mistral Fallback
        ├── PDF: extract text via pdf-parse → ground Mistral on text
        └── Image: send to Pixtral vision model directly
  ↓
Parse JSON → normalize → validate
  ↓
On parse failure: call Mistral repair model → emergency field extraction
  ↓
Return NormalizedAnalysis

Key Methods

Method Description
analyzeContract(fileUrl, options?) Main entry point — full extraction pipeline
askAboutContract(question, contract, userId?) RAG-grounded Q&A on an analyzed contract
preValidateContract(input) Quick pre-check to reject non-contract files
buildAdaptiveContext(userId?) Builds few-shot examples from user's previous contracts

Configuration

Environment Variable Default Description
AI_API_KEY1 / AI_API_KEY2 / AI_API_KEY3 Gemini API keys (rotated automatically)
AI_MODEL_PRIMARY gemini-2.5-flash-preview-04-17 Primary Gemini extraction model
AI_MODEL_SECONDARY_GEMINI Optional secondary Gemini model
AI_EMBEDDING_MODEL text-embedding-004 Gemini embedding model for RAG
MISTRAL_API_KEY Mistral API key (activates fallback)
AI_MODEL_FALLBACK mistral-large-latest Mistral text fallback model
AI_MODEL_MISTRAL_VISION pixtral-large-latest Mistral multimodal model for images
AI_MODEL_MISTRAL_OCR mistral-ocr-latest Mistral OCR model
AI_FORCE_FALLBACK_TEST Set to "1" to bypass Gemini for testing

Sub-modules

File Purpose
ai/analysis.prompt.ts Builds the Gemini extraction prompt with few-shot examples
ai/analysis.parser.ts Robust JSON parser with fence stripping and repair
ai/analysis.normalizer.ts Normalizes raw AI output into NormalizedAnalysis shape
ai/analysis.types.ts TypeScript types for AI input/output
ai/key-manager.ts Round-robin Gemini API key rotation with quota exhaustion detection

Output Schema (NormalizedAnalysis)

{
  title: string | null
  type: ContractType | null           // enum: INSURANCE_AUTO, LOAN, etc.
  provider: string | null
  policyNumber: string | null
  startDate: Date | null
  endDate: Date | null
  premium: number | null
  summary: string
  extractedText: string
  keyPoints: {
    guarantees: string[]
    exclusions: string[]
    franchise: string | null
    importantDates: string[]
    explainability: Array<{
      field: string
      why: string
      sourceSnippet: string
      sourceHints: { page: string|null, section: string|null, confidence: number }
    }>
  }
  keyPeople: Array<{ name: string, role: string|null, email: string|null, phone: string|null }>
  relevantDates: Array<{ date: string, description: string, type: string }>
}

2. RAGService

File: lib/services/rag.service.ts

Purpose

Retrieval-Augmented Generation (RAG) — splits contract text into chunks, generates embeddings, stores them in PostgreSQL, and retrieves the most relevant chunks when answering user questions.

How It Works

analyzeContract completes
  ↓
RAGService.indexContract(contractId, extractedText)
  ├── Split text into 512-token chunks with 64-token overlap
  ├── Generate embeddings via Gemini text-embedding-004
  └── Store in ContractRagChunk table

User asks question
  ↓
RAGService.query(contractId, question)
  ├── Embed the question via text-embedding-004
  ├── Load all chunks for this contract from DB
  ├── Compute cosine similarity between question embedding and each chunk
  └── Return top-K most relevant chunks

Key Methods

Method Description
indexContract(contractId, text) Chunks, embeds, and stores contract text
query(contractId, question, topK?) Returns most similar chunks to a question
deleteIndex(contractId) Removes all RAG chunks for a contract

Storage

Chunks are stored in the ContractRagChunk model:

  • content — raw chunk text
  • embedding — float array (Gemini embedding vector)
  • contentHash — SHA-256 of content (deduplication)
  • chunkIndex — position in original document

3. BlockchainService

File: lib/services/blockchain.service.ts

Purpose

Server-side Ethereum integration. Computes SHA-256 hashes of documents and registers them on the DocumentRegistry smart contract, providing tamper-proof, timestamped proof-of-deposit.

Important: This service uses a server-side wallet (JsonRpcProvider + Wallet). Users do NOT need MetaMask or any wallet.

How It Works

Contract file URL
  ↓
BlockchainService.hashDocument(fileUrl)
  ├── Downloads raw file bytes from UploadThing CDN
  └── Computes SHA-256 → returns 0x-prefixed bytes32 string

  ↓
BlockchainService.registerOnChain(documentHash, fileName)
  ├── Calls contract.registerDocument(bytes32) via server wallet
  ├── Waits for transaction to be mined (1 confirmation)
  ├── Gets block timestamp → this becomes the legal proof date
  └── Returns BlockchainProof { txHash, blockNumber, blockTimestamp, ... }

Configuration

Environment Variable Description
BLOCKCHAIN_NETWORK "hardhat" (dev) or "sepolia" (production)
BLOCKCHAIN_RPC_URL JSON-RPC endpoint (Sepolia public RPC or Alchemy/Infura)
BLOCKCHAIN_PRIVATE_KEY Private key of the signing wallet (holds Sepolia ETH)
BLOCKCHAIN_CONTRACT_ADDRESS Deployed DocumentRegistry contract address

Key Methods

Method Description
hashDocument(fileUrl) Downloads file and computes SHA-256
registerOnChain(hash, fileName) Registers hash on the smart contract
hashAndRegister(fileUrl, fileName) Convenience: hash + register in one call
verifyOnChain(documentHash) Read-only verification of a hash
getNetworkStats() Returns network status, block number, total docs
isConfigured() Returns false if env vars are missing (graceful degradation)

Graceful Degradation

If BLOCKCHAIN_CONTRACT_ADDRESS or BLOCKCHAIN_PRIVATE_KEY are not set, isConfigured() returns false and the app skips blockchain registration silently. The rest of the analysis pipeline completes normally.


4. ContractService

File: lib/services/contract.service.ts

Purpose

CRUD operations and lifecycle management for Contract records. Acts as the single point of interaction with the Contract Prisma model.

Status Machine

UPLOADED → PROCESSING → COMPLETED
                   └──→ FAILED
Status Meaning
UPLOADED File received, waiting for analysis
PROCESSING AI analysis in progress
COMPLETED Analysis done, blockchain registered
FAILED Analysis failed (error stored)

Key Methods

Method Description
create(userId, fileData) Creates a new Contract record (status: UPLOADED)
updateWithAIResults(id, analysis) Saves AI-extracted fields, sets status COMPLETED
updateBlockchainProof(id, proof) Saves blockchain transaction details
setProcessing(id) Sets status to PROCESSING
setFailed(id, error) Sets status to FAILED with error message
getById(id, userId) Fetches a contract, validates ownership
listForUser(userId, filters?) Returns all contracts for a user
deleteContract(id, userId) Deletes contract + cascades to chunks/notifications

5. NotificationService

File: lib/services/notification.service.ts

Purpose

Creates, reads, and manages in-app notifications. Also schedules deadline-based alerts for contracts nearing expiration.

Notification Types

Type Trigger
SUCCESS Contract analysis completed
ERROR Analysis failed
INFO Blockchain registration, general events
WARNING Contract nearing expiry (30 days)
DEADLINE Contract expires in < 7 days

Key Methods

Method Description
createNotification(userId, data) Creates an in-app notification
getUnread(userId) Returns all unread notifications
markAsRead(id, userId) Marks a notification as read
markAllAsRead(userId) Marks all as read
checkDeadlines(userId) Scans contracts nearing expiry and creates alerts
deleteExpired() Purges expired notifications

6. EmailService

File: lib/services/email.service.ts

Purpose

Sends transactional emails using Nodemailer over SMTP. Falls back to Ethereal (test inbox) when running outside production and no SMTP credentials are configured.

Configuration

Variable Description
EMAIL_HOST SMTP server hostname (e.g., smtp-relay.brevo.com)
EMAIL_PORT SMTP port (587 for STARTTLS, 465 for SSL)
EMAIL_SECURE true for SSL/TLS, false for STARTTLS
EMAIL_USER SMTP username
EMAIL_PASS SMTP password
MAIL_FROM From address, e.g., LexiChain <no-reply@yourdomain.com>

Email Types Sent

Email Trigger
Contract analysis complete AI analysis finishes successfully
Analysis failed AI analysis encounters an error
Contract expiry warning Contract expires within 30 days
Contract expiry imminent Contract expires within 7 days
Blockchain registration Document hash registered on-chain

Dev Fallback

If EMAIL_HOST is not set and NODE_ENV !== production, the service creates an Ethereal test account automatically. The preview URL is printed to the server console.


7. StatsService

File: lib/services/stats.service.ts

Purpose

Computes aggregated analytics for the dashboard. All queries run server-side via Prisma.

Metrics Provided

Metric Description
Total contracts Count of all user contracts
By status Breakdown: UPLOADED / PROCESSING / COMPLETED / FAILED
By type Distribution across ContractType enum values
By month Monthly upload trend (last 12 months)
Expiring soon Contracts expiring within 30/90 days
Average premium Average premium across completed contracts
Blockchain coverage % of contracts with on-chain proof
Recent activity Last N contracts with status

8. CertificateService

File: lib/services/certificate.service.ts

Purpose

Generates downloadable PDF certificates of document registration using PDFKit. The certificate includes:

  • Contract metadata (title, type, dates, provider)
  • Document SHA-256 hash
  • Blockchain transaction hash and block number
  • Registration timestamp (block timestamp)
  • QR code linking to Etherscan (Sepolia)

Key Methods

Method Description
generateCertificate(contract) Returns a PDF Buffer for a completed+blockchain contract
generateCertificateStream(contract) Returns a Node.js stream for streaming response

9. StorageService

File: lib/services/storage.service.ts

Purpose

Thin wrapper around UploadThing. Provides utilities for:

  • Deleting uploaded files (cleanup on contract delete)
  • Building file access URLs
  • Validating file constraints

Configuration

UploadThing is configured in lib/upload.ts and exposed via app/api/uploadthing/.

Variable Description
UPLOADTHING_TOKEN UploadThing authentication token
UPLOADTHING_APP_ID UploadThing application ID