diff --git a/chatapi/src/models/chat.model.ts b/chatapi/src/models/chat.model.ts index 23fde46663..b606a6e56b 100644 --- a/chatapi/src/models/chat.model.ts +++ b/chatapi/src/models/chat.model.ts @@ -1,4 +1,4 @@ -type ProviderName = 'openai' | 'perplexity' | 'deepseek' | 'gemini'; +export type ProviderName = 'openai' | 'perplexity' | 'deepseek' | 'gemini'; export interface AIProvider { name: ProviderName; diff --git a/chatapi/src/models/db-doc.model.ts b/chatapi/src/models/db-doc.model.ts index 2992372dfc..9cee15656e 100644 --- a/chatapi/src/models/db-doc.model.ts +++ b/chatapi/src/models/db-doc.model.ts @@ -1,4 +1,4 @@ -import { ChatItem } from './chat.model'; +import { ChatItem, ProviderName } from './chat.model'; export interface DbDoc { _id: string; @@ -6,7 +6,7 @@ export interface DbDoc { user: any; title: string; createdDate: number; - aiProvider: string; + aiProvider?: ProviderName; conversations: ChatItem[]; } diff --git a/chatapi/src/services/chat.service.ts b/chatapi/src/services/chat.service.ts index 3295c21967..34150de158 100644 --- a/chatapi/src/services/chat.service.ts +++ b/chatapi/src/services/chat.service.ts @@ -3,7 +3,7 @@ import { DocumentInsertResponse } from 'nano'; import { chatDB } from '../config/nano.config'; import { retrieveChatHistory } from '../utils/db.utils'; import { aiChat } from '../utils/chat.utils'; -import { AIProvider, ChatMessage } from '../models/chat.model'; +import { AIProvider, ChatMessage, ProviderName } from '../models/chat.model'; function handleChatError(error: any) { if (error.response) { @@ -26,7 +26,11 @@ export async function chat(data: any, stream?: boolean, callback?: (response: st } | undefined> { const { content, ...dbData } = data; const messages: ChatMessage[] = []; - const aiProvider = dbData.aiProvider as AIProvider || { 'name': 'openai' }; + const aiProviderDetails = dbData.aiProvider as AIProvider | ProviderName | undefined; + const fallbackProvider: ProviderName = 'openai'; + const aiProvider: AIProvider = typeof aiProviderDetails === 'object' && aiProviderDetails !== null + ? { name: aiProviderDetails.name, model: aiProviderDetails.model } + : { name: aiProviderDetails ?? fallbackProvider }; if (!content || typeof content !== 'string') { throw new Error('"data.content" is a required non-empty string field'); diff --git a/src/app/chat/chat-sidebar/chat-sidebar.component.ts b/src/app/chat/chat-sidebar/chat-sidebar.component.ts index ac1260cb8c..d7310aea32 100644 --- a/src/app/chat/chat-sidebar/chat-sidebar.component.ts +++ b/src/app/chat/chat-sidebar/chat-sidebar.component.ts @@ -38,7 +38,7 @@ export class ChatSidebarComponent implements OnInit, OnDestroy { selectedConversation: Conversation; lastRenderedConversation: number; isEditing: boolean; - provider: AIProvider; + provider: AIProvider = { name: 'openai' }; fullTextSearch = false; searchType: 'questions' | 'responses'; overlayOpen = false; diff --git a/src/app/chat/chat-window/chat-window.component.ts b/src/app/chat/chat-window/chat-window.component.ts index 3e90c7c9f9..3574f092ff 100644 --- a/src/app/chat/chat-window/chat-window.component.ts +++ b/src/app/chat/chat-window/chat-window.component.ts @@ -30,7 +30,7 @@ export class ChatWindowComponent implements OnInit, OnDestroy, AfterViewInit { streaming: boolean; disabled = false; clearChat = true; - provider: AIProvider; + provider: AIProvider = { name: 'openai' }; fallbackConversation: any[] = []; selectedConversationId: any; promptForm: FormGroup; diff --git a/src/app/chat/chat.component.ts b/src/app/chat/chat.component.ts index 4660ab9615..5c54a5bb85 100644 --- a/src/app/chat/chat.component.ts +++ b/src/app/chat/chat.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { ChatService } from '../shared/chat.service'; -import { AIProvider } from './chat.model'; +import { AIProvider, ProviderName } from './chat.model'; @Component({ selector: 'planet-chat', @@ -10,7 +10,7 @@ import { AIProvider } from './chat.model'; styleUrls: [ './chat.scss' ] }) export class ChatComponent implements OnInit { - activeService: string; + activeService?: ProviderName; aiServices: AIProvider[] = []; displayToggle: boolean; @@ -23,9 +23,11 @@ export class ChatComponent implements OnInit { ngOnInit() { this.chatService.listAIProviders().subscribe((providers) => { this.aiServices = providers; - this.activeService = this.aiServices[0]?.model; + this.activeService = this.aiServices[0]?.name; this.displayToggle = this.aiServices.length > 0; - this.chatService.toggleAIServiceSignal(this.activeService); + if (this.activeService) { + this.chatService.toggleAIServiceSignal(this.activeService); + } }); this.subscribeToAIService(); } @@ -45,7 +47,9 @@ export class ChatComponent implements OnInit { } toggleAIService(): void { - this.chatService.toggleAIServiceSignal(this.activeService); + if (this.activeService) { + this.chatService.toggleAIServiceSignal(this.activeService); + } } } diff --git a/src/app/chat/chat.model.ts b/src/app/chat/chat.model.ts index 28c4b9c883..99206cee9a 100644 --- a/src/app/chat/chat.model.ts +++ b/src/app/chat/chat.model.ts @@ -28,7 +28,7 @@ export interface Message { export type ProviderName = 'openai' | 'perplexity' | 'deepseek' | 'gemini'; export interface AIProvider { - name: string; + name: ProviderName; model?: string; } diff --git a/src/app/shared/chat.service.ts b/src/app/shared/chat.service.ts index 5ed604161b..acb295781f 100644 --- a/src/app/shared/chat.service.ts +++ b/src/app/shared/chat.service.ts @@ -6,7 +6,7 @@ import { catchError, map } from 'rxjs/operators'; import { environment } from '../../environments/environment'; import { findDocuments, inSelector } from '../shared/mangoQueries'; import { CouchService } from '../shared/couchdb.service'; -import { AIServices, AIProvider } from '../chat/chat.model'; +import { AIServices, AIProvider, ProviderName } from '../chat/chat.model'; @Injectable({ providedIn: 'root' @@ -20,14 +20,14 @@ import { AIServices, AIProvider } from '../chat/chat.model'; private errorSubject: Subject = new Subject(); private newChatAdded: Subject = new Subject(); private newChatSelected: Subject = new Subject(); - private toggleAIService = new Subject(); + private toggleAIService = new Subject(); private selectedConversationIdSubject = new BehaviorSubject(null); private aiProvidersSubject = new BehaviorSubject>([]); private currentChatAIProvider = new BehaviorSubject(undefined); newChatAdded$ = this.newChatAdded.asObservable(); newChatSelected$ = this.newChatSelected.asObservable(); - toggleAIService$ = this.toggleAIService.asObservable(); + toggleAIService$: Observable = this.toggleAIService.asObservable(); aiProviders$ = this.aiProvidersSubject.asObservable(); selectedConversationId$: Observable = this.selectedConversationIdSubject.asObservable(); currentChatAIProvider$: Observable = this.currentChatAIProvider.asObservable(); @@ -70,7 +70,7 @@ import { AIServices, AIProvider } from '../chat/chat.model'; }), map((services: AIServices) => { if (services) { - return Object.entries(services) + return (Object.entries(services) as [ ProviderName, boolean ][]) .filter(([ _, model ]) => model === true) .map(([ key ]) => ({ name: key, model: key })); } else { @@ -129,7 +129,7 @@ import { AIServices, AIProvider } from '../chat/chat.model'; this.newChatSelected.next(); } - toggleAIServiceSignal(aiService: string) { + toggleAIServiceSignal(aiService: ProviderName) { this.toggleAIService.next(aiService); }