| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- /**
- * Lego 03 / Lego 06 — RAG Chat via NestJS Socket Proxy
- *
- * Sends chat messages to NestJS over the existing /vision Socket.io connection.
- * NestJS forwards the message server-to-server to n8n (no CORS constraint),
- * then emits chat:result back here.
- *
- * This service does NOT open a second socket — it shares the /vision namespace
- * socket with VisionSocketService by injecting VisionSocketService and calling
- * its exposed socket methods. Instead, we open our own /vision socket here
- * specifically for chat so lifecycle is independently managed.
- *
- * Events:
- * emit → chat:send { message: string }
- * on ← chat:result <n8n response object>
- * on ← chat:error { message: string }
- */
- import { Injectable, signal, OnDestroy } from '@angular/core';
- import { io, Socket } from 'socket.io-client';
- import { environment } from '../../environments/environment';
- export interface ChatResult {
- output?: string;
- answer?: string;
- response?: string;
- text?: string;
- [key: string]: any;
- }
- @Injectable({ providedIn: 'root' })
- export class ChatSocketService implements OnDestroy {
- readonly sending = signal<boolean>(false);
- readonly lastError = signal<string | null>(null);
- private socket: Socket;
- constructor() {
- this.socket = io(`${environment.nestWsUrl}/vision`, {
- transports: ['websocket'],
- reconnection: true,
- reconnectionDelay: 1000,
- secure: true,
- rejectUnauthorized: false,
- });
- }
- /**
- * Send a chat message to NestJS. NestJS proxies it to n8n server-to-server.
- * Returns a Promise that resolves with the n8n response or rejects on error.
- */
- send(message: string): Promise<ChatResult> {
- return new Promise((resolve, reject) => {
- this.sending.set(true);
- this.lastError.set(null);
- // One-shot listeners — removed immediately after first fire
- const onResult = (data: ChatResult) => {
- cleanup();
- this.sending.set(false);
- resolve(data);
- };
- const onError = (err: { message: string }) => {
- cleanup();
- this.sending.set(false);
- const msg = err?.message ?? 'n8n proxy error';
- this.lastError.set(msg);
- reject(new Error(msg));
- };
- const cleanup = () => {
- this.socket.off('chat:result', onResult);
- this.socket.off('chat:error', onError);
- };
- this.socket.once('chat:result', onResult);
- this.socket.once('chat:error', onError);
- this.socket.emit('chat:send', { message });
- });
- }
- clearBackendSession(): void {
- this.socket.emit('chat:clear');
- }
- ngOnDestroy(): void {
- this.socket.disconnect();
- }
- }
|