ci/woodpecker/push/publish Pipeline failed
Co-authored-by: shaggy <[email protected]>
77 lines
2.7 KiB
TypeScript
77 lines
2.7 KiB
TypeScript
import {
|
|
Controller,
|
|
Post,
|
|
Body,
|
|
Logger,
|
|
HttpException,
|
|
HttpStatus,
|
|
NotFoundException,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import { Throttle } from '@nestjs/throttler';
|
|
import { AuthGuard } from '../auth/auth.guard.js';
|
|
import { CurrentUser } from '../auth/current-user.decorator.js';
|
|
import { scopeFromUser, type AuthenticatedUserLike } from '../auth/session-scope.js';
|
|
import { v4 as uuid } from 'uuid';
|
|
import { ChatRequestDto } from './chat.dto.js';
|
|
import { ChatRuntimeRouter } from './chat-runtime-router.js';
|
|
import { ownConversation } from './chat-runtime.js';
|
|
import type { LegacyRuntimeFailure } from './chat-runtime.js';
|
|
|
|
interface ChatResponse {
|
|
conversationId: string;
|
|
text: string;
|
|
}
|
|
|
|
@Controller('api/chat')
|
|
@UseGuards(AuthGuard)
|
|
export class ChatController {
|
|
private readonly logger = new Logger(ChatController.name);
|
|
|
|
constructor(private readonly runtime: ChatRuntimeRouter) {}
|
|
|
|
@Post()
|
|
@Throttle({ default: { limit: 10, ttl: 60_000 } })
|
|
async chat(
|
|
@Body() body: ChatRequestDto,
|
|
@CurrentUser() user: AuthenticatedUserLike,
|
|
): Promise<ChatResponse> {
|
|
const conversationId = body.conversationId ?? uuid();
|
|
const scope = scopeFromUser(user);
|
|
|
|
this.logger.debug(`Handling chat request for user=${user.id}, conversation=${conversationId}`);
|
|
|
|
// The one exclusive runtime owns execution. In legacy mode this reaches the embedded runtime;
|
|
// in pi-rpc it fails closed with `runtime_unsupported` before ever touching embedded execution.
|
|
const result = await this.runtime.completeLegacyRestTurn(
|
|
ownConversation(conversationId, scope),
|
|
{ content: body.content },
|
|
);
|
|
|
|
if (result.ok) {
|
|
return { conversationId, text: result.value.text };
|
|
}
|
|
|
|
throw this.toHttpException(result, conversationId);
|
|
}
|
|
|
|
/** Maps a total {@link LegacyRuntimeFailure} to the fixed browser-safe HTTP surface. */
|
|
private toHttpException(failure: LegacyRuntimeFailure, conversationId: string): HttpException {
|
|
switch (failure.code) {
|
|
case 'conversation_unavailable':
|
|
return new NotFoundException('Session not found');
|
|
case 'request_invalid':
|
|
case 'thinking_level_invalid':
|
|
return new HttpException('Invalid chat request', HttpStatus.BAD_REQUEST);
|
|
case 'timeout':
|
|
return new HttpException('Agent response timed out', HttpStatus.GATEWAY_TIMEOUT);
|
|
case 'runtime_unsupported':
|
|
case 'runtime_unavailable':
|
|
return new HttpException('Agent runtime unavailable', HttpStatus.SERVICE_UNAVAILABLE);
|
|
default:
|
|
this.logger.error(`Chat turn failed for conversation=${conversationId}: ${failure.code}`);
|
|
return new HttpException('Agent processing failed', HttpStatus.INTERNAL_SERVER_ERROR);
|
|
}
|
|
}
|
|
}
|