chat.component.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import { Component, OnInit, OnDestroy, ViewChild, ElementRef, AfterViewChecked } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. import { MatCardModule } from '@angular/material/card';
  5. import { MatInputModule } from '@angular/material/input';
  6. import { MatButtonModule } from '@angular/material/button';
  7. import { MatListModule } from '@angular/material/list';
  8. import { MatSelectModule } from '@angular/material/select';
  9. import { io, Socket } from 'socket.io-client';
  10. import { webConfig } from '../config';
  11. import { ThoughtPayload } from '../interfaces/interface';
  12. interface ChatMessage {
  13. content: string;
  14. sender: 'user' | 'bot';
  15. }
  16. @Component({
  17. selector: 'app-chat',
  18. standalone: true,
  19. imports: [
  20. CommonModule,
  21. FormsModule,
  22. MatCardModule,
  23. MatInputModule,
  24. MatButtonModule,
  25. MatListModule,
  26. MatSelectModule
  27. ],
  28. templateUrl: './chat.component.html',
  29. styleUrls: ['./chat.component.css']
  30. })
  31. export class ChatComponent implements OnInit, OnDestroy, AfterViewChecked {
  32. @ViewChild('scrollContainer') private scrollContainer!: ElementRef;
  33. messages: ChatMessage[] = [];
  34. inputMessage = '';
  35. loading = false;
  36. agentThoughts: ThoughtPayload[] = [];
  37. // Models
  38. models: ('openai' | 'gemini')[] = ['openai', 'gemini'];
  39. currentProvider: 'openai' | 'gemini' = 'openai';
  40. modelName: string = ''
  41. private socket!: Socket;
  42. ngOnInit() {
  43. this.initSocketConnection();
  44. }
  45. ngOnDestroy() {
  46. if (this.socket) this.socket.disconnect();
  47. }
  48. ngAfterViewChecked() {
  49. this.scrollToBottom();
  50. }
  51. private initSocketConnection() {
  52. this.socket = io(`${webConfig.exposedUrl}/ffb`);
  53. this.socket.on('connect', () => {
  54. console.log('Connected to FFB Gateway');
  55. // Request current model for this session
  56. this.socket.emit('get_model', {}, (res: any) => {
  57. // nothing happens. Response shoudl be in 'current model' event
  58. });
  59. });
  60. this.socket.on('agent_thought', (payload: ThoughtPayload) => {
  61. this.agentThoughts.push(payload);
  62. });
  63. this.socket.on('chat_response', (payload: { message: string }) => {
  64. this.messages.push({ content: payload.message, sender: 'bot' });
  65. this.loading = false;
  66. });
  67. this.socket.on('current_model', (data: any) => {
  68. // Update dropdown if server sends a model change
  69. if (data?.provider) {
  70. this.currentProvider = data.provider;
  71. this.modelName = data.modelName
  72. }
  73. });
  74. this.socket.on('error', (err) => console.error('Socket error:', err));
  75. }
  76. sendMessage() {
  77. const trimmedMessage = this.inputMessage.trim();
  78. if (!trimmedMessage || this.loading) return;
  79. this.messages.push({ content: trimmedMessage, sender: 'user' });
  80. this.loading = true;
  81. this.agentThoughts = [];
  82. this.socket.emit('chat', { message: trimmedMessage });
  83. this.inputMessage = '';
  84. }
  85. switchModel(model: 'openai' | 'gemini') {
  86. console.log('Switching model to:', model); // Debug log
  87. if (model === this.currentProvider) return; // Prevent switching to same model
  88. // Emit to backend
  89. this.socket.emit('switch_model', { provider: model }, (res: any) => {
  90. console.log('Switching model:', res?.data?.provider);
  91. });
  92. this.socket.emit('get_model', {}, (res: any) => {
  93. });
  94. }
  95. // Called by mat-select on selection change
  96. onModelSelect(event: any) {
  97. console.log('Dropdown selection:', event.value); // Debug log
  98. this.switchModel(event.value);
  99. }
  100. private scrollToBottom(): void {
  101. try {
  102. const el = this.scrollContainer.nativeElement;
  103. el.scrollTop = el.scrollHeight;
  104. } catch { }
  105. }
  106. // --- Template Helpers ---
  107. asString(key: unknown): string {
  108. return String(key);
  109. }
  110. shouldShowAttribute(key: unknown, value: any): boolean {
  111. const sKey = String(key);
  112. return !['node', 'status'].includes(sKey) && value != null && value !== '';
  113. }
  114. isObject(value: any): boolean {
  115. return value !== null && typeof value === 'object';
  116. }
  117. }