All files / src/session-inputs session-inputs.controller.ts

77.77% Statements 21/27
100% Branches 0/0
40% Functions 4/10
76% Lines 19/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 2016x                       6x 6x 6x           6x       6x 6x                       6x       4x                     6x                       6x                                               6x       2x                               6x                           6x                                         6x                                             6x                                           6x         8x              
import {
  Controller,
  Post,
  Body,
  Param,
  Get,
  ParseUUIDPipe,
  HttpCode,
  HttpStatus,
  Delete,
  Patch,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
import { SessionInputsService } from './session-inputs.service';
import {
  CreateSessionInputDto,
  UpdateSessionInputDto,
  UpdateSessionInputContentDto,
  BatchToggleDiscardedDto,
} from './dto/session-input.dto';
import { SessionInput } from '../core-entities';
 
@ApiTags('session-inputs')
@Controller('sessions/:sessionId/inputs')
export class SessionInputsController {
  constructor(private readonly sessionInputsService: SessionInputsService) {}
 
  @Post()
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Create a new input (user prompt) in session' })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiResponse({
    status: 201,
    description: 'Input created successfully',
    type: SessionInput,
  })
  @ApiResponse({ status: 404, description: 'Session not found' })
  async create(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
    @Body() createSessionInputDto: CreateSessionInputDto,
  ): Promise<SessionInput> {
    return this.sessionInputsService.create(sessionId, createSessionInputDto);
  }
 
  @Get()
  @ApiOperation({ summary: 'List all inputs for a session' })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiResponse({
    status: 200,
    description: 'List of inputs',
    type: [SessionInput],
  })
  async findAll(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
  ): Promise<SessionInput[]> {
    return this.sessionInputsService.findAllBySessionId(sessionId);
  }
 
  @Get(':inputId')
  @ApiOperation({ summary: 'Get a specific input by ID' })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiParam({ name: 'inputId', description: 'Input UUID', format: 'uuid' })
  @ApiResponse({ status: 200, description: 'Input found', type: SessionInput })
  @ApiResponse({ status: 404, description: 'Input or session not found' })
  async findOne(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
    @Param('inputId', ParseUUIDPipe) inputId: string,
  ): Promise<SessionInput> {
    return this.sessionInputsService.findOne(sessionId, inputId);
  }
 
  @Patch('batch-toggle-discarded')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Batch toggle discarded status for inputs',
    description:
      'Toggle is_discarded flag for multiple inputs to exclude/include them from LLM context. Discarded inputs are preserved in database but excluded from chat history sent to LLM.',
  })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiResponse({
    status: 200,
    description: 'Inputs toggled successfully',
    schema: { type: 'object', properties: { affected: { type: 'number' } } },
  })
  @ApiResponse({
    status: 404,
    description: 'No inputs found with provided IDs',
  })
  async toggleDiscardedBatch(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
    @Body() dto: BatchToggleDiscardedDto,
  ): Promise<{ affected: number }> {
    return this.sessionInputsService.toggleDiscardedBatch(sessionId, dto);
  }
 
  @Delete(':inputId/until-here')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({
    summary: 'Delete this input and all subsequent inputs (rollback history)',
  })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiParam({
    name: 'inputId',
    description: 'Input UUID to delete from',
    format: 'uuid',
  })
  @ApiResponse({ status: 204, description: 'Inputs deleted successfully' })
  @ApiResponse({ status: 404, description: 'Input or session not found' })
  async removeUntilHere(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
    @Param('inputId', ParseUUIDPipe) inputId: string,
  ): Promise<void> {
    return this.sessionInputsService.removeUntilHere(sessionId, inputId);
  }
 
  @Delete(':inputId')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Delete a single input' })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiParam({ name: 'inputId', description: 'Input UUID', format: 'uuid' })
  @ApiResponse({ status: 204, description: 'Input deleted successfully' })
  @ApiResponse({ status: 404, description: 'Input or session not found' })
  async remove(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
    @Param('inputId', ParseUUIDPipe) inputId: string,
  ): Promise<void> {
    return this.sessionInputsService.remove(sessionId, inputId);
  }
 
  @Patch(':inputId/retry')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary:
      'Retry an input with modified prompt (regenerate context & LLM call)',
  })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiParam({ name: 'inputId', description: 'Input UUID', format: 'uuid' })
  @ApiResponse({
    status: 200,
    description: 'Input retried successfully',
    type: SessionInput,
  })
  @ApiResponse({ status: 404, description: 'Input or session not found' })
  async retry(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
    @Param('inputId', ParseUUIDPipe) inputId: string,
    @Body() updateSessionInputDto: UpdateSessionInputDto,
  ): Promise<SessionInput> {
    return this.sessionInputsService.retry(
      sessionId,
      inputId,
      updateSessionInputDto,
    );
  }
 
  @Post(':inputId/regenerate')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Regenerate AI response for an existing input' })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiParam({ name: 'inputId', description: 'Input UUID', format: 'uuid' })
  @ApiResponse({
    status: 200,
    description: 'AI response regenerated',
    type: SessionInput,
  })
  @ApiResponse({ status: 404, description: 'Input or session not found' })
  async regenerateAiResponse(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
    @Param('inputId', ParseUUIDPipe) inputId: string,
  ): Promise<SessionInput> {
    return this.sessionInputsService.regenerateAiResponse(sessionId, inputId);
  }
 
  @Patch(':inputId')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Update input content (silent edit)',
    description:
      'Updates message content without triggering LLM regeneration. Use for redacting sensitive data or compressing session history. Does NOT regenerate context or call LLM.',
  })
  @ApiParam({ name: 'sessionId', description: 'Session UUID', format: 'uuid' })
  @ApiParam({ name: 'inputId', description: 'Input UUID', format: 'uuid' })
  @ApiResponse({
    status: 200,
    description: 'Input updated',
    type: SessionInput,
  })
  @ApiResponse({ status: 404, description: 'Input or session not found' })
  async update(
    @Param('sessionId', ParseUUIDPipe) sessionId: string,
    @Param('inputId', ParseUUIDPipe) inputId: string,
    @Body() updateDto: UpdateSessionInputContentDto,
  ): Promise<SessionInput> {
    return this.sessionInputsService.updateContent(
      sessionId,
      inputId,
      updateDto,
    );
  }
}