File

src/ai/ai.controller.ts

Prefix

ai

Description

Intelligence Center endpoints.

Guard chain (order matters):

  1. CombinedAuthGuard — JWT or API key, populates request.user
  2. RolesGuard — enforces @Roles() decorators
  3. AiEnabledGuard — blocks all calls when org disabled the toggle

Throttling: every endpoint has a tight per-IP cap because each call can incur real third-party LLM costs. The global throttler is a much looser fallback.

Index

Methods

Methods

Async detectAnomalies
detectAnomalies(orgId: string)
Decorators :
@Get('anomalies')
@Throttle({default: undefined})
@ApiOperation({summary: 'Heuristic anomaly scan over recent operations'})
Parameters :
Name Type Optional
orgId string No
Returns : unknown
Async getEta
getEta(orgId: string, tripId: string)
Decorators :
@Get('eta/:tripId')
@Throttle({default: undefined})
@ApiOperation({summary: 'Predictive ETA for a trip (org-scoped)'})
Parameters :
Name Type Optional
orgId string No
tripId string No
Returns : unknown
Async getUsage
getUsage(orgId: string, days?: string)
Decorators :
@Get('usage')
@Roles('SUPER_ADMIN', 'ADMIN')
@ApiOperation({summary: 'Intelligence Center usage + cost report (admin)'})
Parameters :
Name Type Optional
orgId string No
days string Yes
Returns : unknown
Async ocrDocument
ocrDocument(orgId: string, userId: string, file: Express.Multer.File, req: Request)
Decorators :
@Post('ocr')
@Throttle({default: undefined})
@UseInterceptors(undefined)
@ApiConsumes('multipart/form-data')
@ApiOperation({summary: 'Extract structured data from a document image'})
Parameters :
Name Type Optional
orgId string No
userId string No
file Express.Multer.File No
req Request No
Returns : unknown
Async optimizeRoutes
optimizeRoutes(orgId: string)
Decorators :
@Get('optimize-routes')
@Throttle({default: undefined})
@ApiOperation({summary: 'Smart route consolidation suggestions'})
Parameters :
Name Type Optional
orgId string No
Returns : unknown
Async query
query(orgId: string, userId: string, dto: QueryDto, req: Request)
Decorators :
@Post('query')
@Throttle({default: undefined})
@ApiOperation({summary: 'Natural-language question about fleet data'})
Parameters :
Name Type Optional
orgId string No
userId string No
dto QueryDto No
req Request No
Returns : unknown
import {
  BadRequestException,
  Body,
  Controller,
  Get,
  Param,
  Post,
  Query,
  Req,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { Request } from 'express';
import { FileInterceptor } from '@nestjs/platform-express';
import { Throttle } from '@nestjs/throttler';
import {
  ApiBearerAuth,
  ApiConsumes,
  ApiOperation,
  ApiTags,
} from '@nestjs/swagger';
import { AiService } from './ai.service';
import { AiEnabledGuard } from './ai-enabled.guard';
import { CombinedAuthGuard } from '../auth/guards/combined-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { QueryDto } from './dto/query.dto';

/**
 * Intelligence Center endpoints.
 *
 * Guard chain (order matters):
 *   1. CombinedAuthGuard — JWT or API key, populates request.user
 *   2. RolesGuard         — enforces @Roles() decorators
 *   3. AiEnabledGuard     — blocks all calls when org disabled the toggle
 *
 * Throttling: every endpoint has a tight per-IP cap because each call
 * can incur real third-party LLM costs. The global throttler is a much
 * looser fallback.
 */
@ApiTags('Intelligence')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard, RolesGuard, AiEnabledGuard)
@Controller('ai')
export class AiController {
  constructor(private readonly aiService: AiService) {}

  // ── Natural language query ───────────────────────────────────────────
  @Post('query')
  @Throttle({ default: { limit: 20, ttl: 60_000 } }) // 20/min/IP
  @ApiOperation({ summary: 'Natural-language question about fleet data' })
  async query(
    @CurrentUser('organizationId') orgId: string,
    @CurrentUser('id') userId: string,
    @Body() dto: QueryDto,
    @Req() req: Request,
  ) {
    const ip = (req.headers['x-forwarded-for'] as string) || req.ip || 'unknown';
    return this.aiService.naturalLanguageQuery(orgId, userId, dto.question, ip);
  }

  // ── Predictive ETA ───────────────────────────────────────────────────
  @Get('eta/:tripId')
  @Throttle({ default: { limit: 60, ttl: 60_000 } }) // 60/min/IP
  @ApiOperation({ summary: 'Predictive ETA for a trip (org-scoped)' })
  async getEta(
    @CurrentUser('organizationId') orgId: string,
    @Param('tripId') tripId: string,
  ) {
    return this.aiService.predictEta(orgId, tripId);
  }

  // ── Route optimizer ──────────────────────────────────────────────────
  @Get('optimize-routes')
  @Throttle({ default: { limit: 30, ttl: 60_000 } })
  @ApiOperation({ summary: 'Smart route consolidation suggestions' })
  async optimizeRoutes(@CurrentUser('organizationId') orgId: string) {
    return this.aiService.optimizeRoutes(orgId);
  }

  // ── Anomaly detection ────────────────────────────────────────────────
  @Get('anomalies')
  @Throttle({ default: { limit: 60, ttl: 60_000 } })
  @ApiOperation({ summary: 'Heuristic anomaly scan over recent operations' })
  async detectAnomalies(@CurrentUser('organizationId') orgId: string) {
    return this.aiService.detectAnomalies(orgId);
  }

  // ── Document Intelligence (OCR) ──────────────────────────────────────
  @Post('ocr')
  @Throttle({ default: { limit: 10, ttl: 60_000 } }) // 10/min — vision is expensive
  @UseInterceptors(
    FileInterceptor('file', {
      limits: { fileSize: 10 * 1024 * 1024, files: 1 },
    }),
  )
  @ApiConsumes('multipart/form-data')
  @ApiOperation({ summary: 'Extract structured data from a document image' })
  async ocrDocument(
    @CurrentUser('organizationId') orgId: string,
    @CurrentUser('id') userId: string,
    @UploadedFile() file: Express.Multer.File,
    @Req() req: Request,
  ) {
    if (!file) throw new BadRequestException('No file uploaded');
    const ip = (req.headers['x-forwarded-for'] as string) || req.ip || 'unknown';
    return this.aiService.extractFromDocument(orgId, userId, file, ip);
  }

  // ── Usage stats (admin only) ─────────────────────────────────────────
  @Get('usage')
  @Roles('SUPER_ADMIN', 'ADMIN')
  @ApiOperation({ summary: 'Intelligence Center usage + cost report (admin)' })
  async getUsage(
    @CurrentUser('organizationId') orgId: string,
    @Query('days') days?: string,
  ) {
    const window = Math.min(Math.max(parseInt(days || '30', 10) || 30, 1), 365);
    return this.aiService.getUsageStats(orgId, window);
  }
}

results matching ""

    No results matching ""