Ver código fonte

updated to be able to switch between models

Dr-Swopt 1 semana atrás
pai
commit
e181454263

+ 26 - 1
src/app/chat/chat.component.css

@@ -260,4 +260,29 @@
 
 .log-area::-webkit-scrollbar-track { background: #1e1e1e; }
 .log-area::-webkit-scrollbar-thumb { background: #444; border-radius: 10px; }
-.message-area::-webkit-scrollbar-thumb { background: #ccc; border-radius: 10px; }
+.message-area::-webkit-scrollbar-thumb { background: #ccc; border-radius: 10px; }
+
+/* --- Model Switcher Dropdown --- */
+.model-switcher {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 12px 24px;
+  border-bottom: 1px solid #e0e0e0;
+  background-color: #fafafa;
+}
+
+.model-switcher span {
+  font-weight: 600;
+  font-size: 0.9rem;
+}
+
+.model-dropdown {
+  width: 180px;
+}
+
+.current-label {
+  font-size: 0.75rem;
+  color: #888;
+  margin-left: 6px;
+}

+ 23 - 9
src/app/chat/chat.component.html

@@ -1,10 +1,29 @@
 <div class="chat-container">
+  <!-- Left Panel: Chat -->
   <mat-card class="chat-card">
     <div class="card-header">
       <h2>Chat</h2>
       <span class="status-indicator" [class.active]="loading"></span>
     </div>
 
+    <!-- Model Switcher -->
+    <div class="model-switcher">
+      <!-- Dropdown -->
+      <mat-form-field appearance="outline" class="model-dropdown">
+        <mat-select (selectionChange)="onModelSelect($event)">
+          <mat-option *ngFor="let m of models" [value]="m" [disabled]="m === currentProvider">
+            {{ m | titlecase }}
+            <span *ngIf="m === currentProvider" class="current-label">(In Use)</span>
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+
+      <!-- Current Model Info -->
+      <div class="model-info">
+        <span>Provider: <strong>{{ currentProvider }}</strong></span>
+        <span *ngIf="modelName">Model: <strong>{{ modelName }}</strong></span>
+      </div>
+    </div>
     <mat-card-content class="message-area" #scrollContainer>
       <div *ngFor="let msg of messages" [ngClass]="['message-bubble', msg.sender]">
         <div class="content">{{ msg.content }}</div>
@@ -18,21 +37,16 @@
 
     <mat-card-actions class="input-section">
       <mat-form-field appearance="outline" subscriptSizing="dynamic">
-        <input matInput 
-               placeholder="Ask anything..." 
-               [(ngModel)]="inputMessage" 
-               (keyup.enter)="sendMessage()" 
-               [disabled]="loading" />
+        <input matInput placeholder="Ask anything..." [(ngModel)]="inputMessage" (keyup.enter)="sendMessage()"
+          [disabled]="loading" />
       </mat-form-field>
-      <button mat-flat-button 
-              color="primary" 
-              (click)="sendMessage()" 
-              [disabled]="!inputMessage.trim() || loading">
+      <button mat-flat-button color="primary" (click)="sendMessage()" [disabled]="!inputMessage.trim() || loading">
         Send
       </button>
     </mat-card-actions>
   </mat-card>
 
+  <!-- Right Panel: Trace & Logic -->
   <mat-card class="agent-card">
     <div class="card-header">
       <h2>Trace & Logic</h2>

+ 55 - 25
src/app/chat/chat.component.ts

@@ -5,6 +5,7 @@ import { MatCardModule } from '@angular/material/card';
 import { MatInputModule } from '@angular/material/input';
 import { MatButtonModule } from '@angular/material/button';
 import { MatListModule } from '@angular/material/list';
+import { MatSelectModule } from '@angular/material/select';
 import { io, Socket } from 'socket.io-client';
 import { webConfig } from '../config';
 import { ThoughtPayload } from '../interfaces/interface';
@@ -17,21 +18,30 @@ interface ChatMessage {
 @Component({
   selector: 'app-chat',
   standalone: true,
-  imports: [CommonModule, FormsModule, MatCardModule, MatInputModule, MatButtonModule, MatListModule],
+  imports: [
+    CommonModule,
+    FormsModule,
+    MatCardModule,
+    MatInputModule,
+    MatButtonModule,
+    MatListModule,
+    MatSelectModule
+  ],
   templateUrl: './chat.component.html',
   styleUrls: ['./chat.component.css']
 })
 export class ChatComponent implements OnInit, OnDestroy, AfterViewChecked {
   @ViewChild('scrollContainer') private scrollContainer!: ElementRef;
 
-  // Configuration constants
-  public readonly mandatoryKeys = ['node', 'status'];
-  
   messages: ChatMessage[] = [];
   inputMessage = '';
   loading = false;
   agentThoughts: ThoughtPayload[] = [];
-  
+
+  // Models
+  models: ('openai' | 'gemini')[] = ['openai', 'gemini'];
+  currentProvider: 'openai' | 'gemini' = 'openai';
+  modelName: string = ''
   private socket!: Socket;
 
   ngOnInit() {
@@ -39,16 +49,9 @@ export class ChatComponent implements OnInit, OnDestroy, AfterViewChecked {
   }
 
   ngOnDestroy() {
-    if (this.socket) {
-      this.socket.disconnect();
-      console.log('Socket disconnected and cleaned up.');
-    }
+    if (this.socket) this.socket.disconnect();
   }
 
-  /**
-   * Reality Check: Auto-scrolling is essential for chat. 
-   * This ensures the user doesn't have to manually scroll for every response.
-   */
   ngAfterViewChecked() {
     this.scrollToBottom();
   }
@@ -56,7 +59,14 @@ export class ChatComponent implements OnInit, OnDestroy, AfterViewChecked {
   private initSocketConnection() {
     this.socket = io(`${webConfig.exposedUrl}/ffb`);
 
-    this.socket.on('connect', () => console.log('Connected to FFB Gateway'));
+    this.socket.on('connect', () => {
+      console.log('Connected to FFB Gateway');
+
+      // Request current model for this session
+      this.socket.emit('get_model', {}, (res: any) => {
+        // nothing happens. Response shoudl be in 'current model' event
+      });
+    });
 
     this.socket.on('agent_thought', (payload: ThoughtPayload) => {
       this.agentThoughts.push(payload);
@@ -67,45 +77,65 @@ export class ChatComponent implements OnInit, OnDestroy, AfterViewChecked {
       this.loading = false;
     });
 
+    this.socket.on('current_model', (data: any) => {
+      // Update dropdown if server sends a model change
+      if (data?.provider) {
+        this.currentProvider = data.provider;
+        this.modelName = data.modelName
+      }
+    });
+
     this.socket.on('error', (err) => console.error('Socket error:', err));
   }
 
-  // --- Helper Methods ---
-
   sendMessage() {
     const trimmedMessage = this.inputMessage.trim();
     if (!trimmedMessage || this.loading) return;
 
     this.messages.push({ content: trimmedMessage, sender: 'user' });
     this.loading = true;
-    this.agentThoughts = []; // Reset trace for new turn
+    this.agentThoughts = [];
 
     this.socket.emit('chat', { message: trimmedMessage });
     this.inputMessage = '';
   }
 
+  switchModel(model: 'openai' | 'gemini') {
+    console.log('Switching model to:', model); // Debug log
+    if (model === this.currentProvider) return; // Prevent switching to same model
+
+    // Emit to backend
+    this.socket.emit('switch_model', { provider: model }, (res: any) => {
+      console.log('Switching model:', res?.data?.provider);
+    });
+    this.socket.emit('get_model', {}, (res: any) => {
+    });
+  }
+
+  // Called by mat-select on selection change
+  onModelSelect(event: any) {
+    console.log('Dropdown selection:', event.value); // Debug log
+    this.switchModel(event.value);
+  }
+
   private scrollToBottom(): void {
     try {
       const el = this.scrollContainer.nativeElement;
       el.scrollTop = el.scrollHeight;
-    } catch (err) {}
+    } catch { }
   }
 
-  // --- Template Logic Helpers ---
-
+  // --- Template Helpers ---
   asString(key: unknown): string {
     return String(key);
   }
 
   shouldShowAttribute(key: unknown, value: any): boolean {
     const sKey = String(key);
-    return !this.mandatoryKeys.includes(sKey) && 
-           value !== null && 
-           value !== undefined && 
-           value !== '';
+    return !['node', 'status'].includes(sKey) && value != null && value !== '';
   }
 
   isObject(value: any): boolean {
     return value !== null && typeof value === 'object';
   }
-}
+}