Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chat: add perplexity api (fixes #7409) #7410

Merged
merged 9 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions chatapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"cors": "^2.8.5",
"dotenv": "^16.1.4",
"express": "^4.18.2",
"nano": "^10.1.2",
"openai": "^3.3.0",
"dotenv": "^16.1.4",
"openai": "^4.28.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.3"
},
Expand Down
8 changes: 3 additions & 5 deletions chatapi/src/config/openai.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Configuration, OpenAIApi } from 'openai';
import OpenAI from 'openai';
import dotenv from 'dotenv';

dotenv.config();

const configuration = new Configuration({
'apiKey': process.env.OPENAI_API_KEY,
const openai = new OpenAI({
'apiKey': process.env.OPENAI_API_KEY || '',
});

const openai = new OpenAIApi(configuration);

export default openai;
11 changes: 11 additions & 0 deletions chatapi/src/config/perplexity.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import OpenAI from 'openai';
import dotenv from 'dotenv';

dotenv.config();

const perplexity = new OpenAI({
'apiKey': process.env.PERPLEXITY_API_KEY || '',
'baseURL': 'https://api.perplexity.ai',
});

export default perplexity;
13 changes: 10 additions & 3 deletions chatapi/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ app.get('/', (req: any, res: any) => {

app.post('/', async (req: any, res: any) => {
try {
const { data, save } = req.body;
const { data, save, usePerplexity } = req.body;

if (typeof data !== 'object' || Array.isArray(data) || Object.keys(data).length === 0) {
res.status(400).json({ 'error': 'Bad Request', 'message': 'The "data" field must be a non-empty object' });
}

if (!save) {
const response = await chatNoSave(data.content);
const response = await chatNoSave(data.content, usePerplexity);
res.status(200).json({
'status': 'Success',
'chat': response
});
} else if (save) {
const response = await chat(data);
const response = await chat(data, usePerplexity);
res.status(201).json({
'status': 'Success',
'chat': response?.completionText,
Expand All @@ -48,6 +48,13 @@ app.post('/', async (req: any, res: any) => {
}
});

app.get('/checkproviders', (req: any, res: any) => {
res.status(200).json({
'openai': process.env.OPENAI_API_KEY ? true : false,
'perplexity': process.env.PERPLEXITY_API_KEY ? true : false
});
});

const port = process.env.SERVE_PORT || 5000;

app.listen(port, () => console.log(`Server running on port ${port}`)); // eslint-disable-line no-console
8 changes: 4 additions & 4 deletions chatapi/src/services/chat.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ChatMessage } from '../models/chat-message.model';
* @param data - Chat data including content and additional information
* @returns Object with completion text and CouchDB save response
*/
export async function chat(data: any): Promise<{
export async function chat(data: any, usePerplexity: boolean): Promise<{
completionText: string;
couchSaveResponse: DocumentInsertResponse;
} | undefined> {
Expand All @@ -36,7 +36,7 @@ export async function chat(data: any): Promise<{
messages.push({ 'role': 'user', content });

try {
const completionText = await gptChat(messages);
const completionText = await gptChat(messages, usePerplexity);

dbData.conversations[dbData.conversations.length - 1].response = completionText;

Expand All @@ -54,13 +54,13 @@ export async function chat(data: any): Promise<{
}
}

export async function chatNoSave(content: any): Promise< string | undefined> {
export async function chatNoSave(content: any, usePerplexity: boolean): Promise< string | undefined> {
const messages: ChatMessage[] = [];

messages.push({ 'role': 'user', content });

try {
const completionText = await gptChat(messages);
const completionText = await gptChat(messages, usePerplexity);
messages.push({
'role': 'assistant', 'content': completionText
});
Expand Down
10 changes: 6 additions & 4 deletions chatapi/src/utils/gpt-chat.utils.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import openai from '../config/openai.config';
import perplexity from '../config/perplexity.config';
import { ChatMessage } from '../models/chat-message.model';

/**
* Uses openai's createChatCompletion endpoint to generate chat completions
* @param messages - Array of chat messages
* @returns Completion text
*/
export async function gptChat(messages: ChatMessage[]): Promise<string> {
const completion = await openai.createChatCompletion({
'model': 'gpt-3.5-turbo',
export async function gptChat(messages: ChatMessage[], usePerplexity: boolean): Promise<string> {
const ai = usePerplexity ? perplexity : openai;
const completion = await ai.chat.completions.create({
'model': usePerplexity ? 'pplx-7b-online' : 'gpt-3.5-turbo',
messages,
});

const completionText = completion.data.choices[0]?.message?.content;
const completionText = completion.choices[0]?.message?.content;
if (!completionText) {
throw new Error('Unexpected API response');
}
Expand Down
1 change: 1 addition & 0 deletions docker/chat.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
OPENAI_API_KEY=
PERPLEXITY_API_KEY=
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "planet",
"license": "AGPL-3.0",
"version": "0.14.16",
"version": "0.14.17",
"myplanet": {
"latest": "v0.13.95",
"min": "v0.13.0"
"latest": "v0.14.5",
"min": "v0.13.57"
},
"scripts": {
"ng": "ng",
Expand Down
12 changes: 11 additions & 1 deletion src/app/chat/chat-window/chat-window.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { UserService } from '../../shared/user.service';
export class ChatWindowComponent implements OnInit, OnDestroy {
private onDestroy$ = new Subject<void>();
spinnerOn = true;
usePerplexity: boolean;
conversations: any[] = [];
selectedConversationId: any;
promptForm: FormGroup;
Expand All @@ -40,6 +41,7 @@ export class ChatWindowComponent implements OnInit, OnDestroy {
this.createForm();
this.subscribeToNewChatSelected();
this.subscribeToSelectedConversation();
this.subscribeToAIService();
}

ngOnDestroy() {
Expand Down Expand Up @@ -69,6 +71,14 @@ export class ChatWindowComponent implements OnInit, OnDestroy {
});
}

subscribeToAIService() {
this.chatService.toggleAIService$
.pipe(takeUntil(this.onDestroy$))
.subscribe((aiService => {
this.usePerplexity = aiService === 'perplexity' ? true : false;
}));
}

createForm() {
this.promptForm = this.formBuilder.group({
prompt: [ '', CustomValidators.required ],
Expand Down Expand Up @@ -132,7 +142,7 @@ export class ChatWindowComponent implements OnInit, OnDestroy {

this.setSelectedConversation();

this.chatService.getPrompt(this.data, true).subscribe(
this.chatService.getPrompt(this.data, true, this.usePerplexity).subscribe(
(completion: any) => {
this.conversations.push({ query: content, response: completion?.chat });
this.selectedConversationId = {
Expand Down
9 changes: 9 additions & 0 deletions src/app/chat/chat.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
<button mat-icon-button (click)="goBack()"><mat-icon>arrow_back</mat-icon></button>
<span i18n>AI Chat</span>
<span class="toolbar-fill"></span>
<mat-button-toggle-group *ngIf="displayToggle; else textDisplay" [(ngModel)]="aiService" (change)="toggleAIService()">
<mat-button-toggle value="openai" i18n>Open AI</mat-button-toggle>
<mat-button-toggle value="perplexity" i18n>Perplexity AI</mat-button-toggle>
</mat-button-toggle-group>
<ng-template #textDisplay>
<span *ngIf="aiService === 'openai'" i18n>Open AI</span>
<span *ngIf="aiService === 'perplexity'" i18n>Perplexity AI</span>
</ng-template>

</mat-toolbar>

<div class="space-container">
Expand Down
28 changes: 26 additions & 2 deletions src/app/chat/chat.component.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';

import { ChatService } from '../shared/chat.service';
import { AIServices } from './chat.model';

@Component({
selector: 'planet-chat',
templateUrl: './chat.component.html',
styleUrls: [ './chat.scss' ]
})
export class ChatComponent {
export class ChatComponent implements OnInit {
aiService: 'openai' | 'perplexity';
displayToggle: boolean;

constructor(
private chatService: ChatService,
private route: ActivatedRoute,
private router: Router,
) {}

ngOnInit() {
this.chatService.fetchAIProviders().pipe(
catchError(err => {
console.error(err);
return of({ openai: false, perplexity: false });
})
).subscribe((aiServices: AIServices) => {
this.displayToggle = aiServices.openai && aiServices.perplexity;
this.aiService = aiServices.openai ? 'openai' : 'perplexity';
});
}

goBack(): void {
this.router.navigate([ '/' ], { relativeTo: this.route });
}

toggleAIService(): void {
this.chatService.toggleAIServiceSignal(this.aiService);
}

}
5 changes: 5 additions & 0 deletions src/app/chat/chat.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ export interface Message {
query: string;
response: string;
}

export interface AIServices {
openai: boolean;
perplexity: boolean;
}
15 changes: 13 additions & 2 deletions src/app/shared/chat.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { CouchService } from '../shared/couchdb.service';
private dbName = 'chat_history';
private newChatAdded = new Subject<void>();
private newChatSelected = new Subject<void>();
private toggleAIService = new Subject<string>();
private selectedConversationIdSubject = new BehaviorSubject<object | null>(null);

newChatAdded$ = this.newChatAdded.asObservable();
newChatSelected$ = this.newChatSelected.asObservable();
toggleAIService$ = this.toggleAIService.asObservable();
selectedConversationId$: Observable<object | null> = this.selectedConversationIdSubject.asObservable();


Expand All @@ -25,10 +27,15 @@ import { CouchService } from '../shared/couchdb.service';
private couchService: CouchService
) {}

getPrompt(data: Object, save: boolean): Observable<any> {
fetchAIProviders() {
return this.httpClient.get(`${this.baseUrl}/checkproviders`);
}

getPrompt(data: Object, save: boolean, usePerplexity: boolean): Observable<any> {
return this.httpClient.post(this.baseUrl, {
data,
save
save,
usePerplexity
});
}

Expand All @@ -44,6 +51,10 @@ import { CouchService } from '../shared/couchdb.service';
this.newChatSelected.next();
}

toggleAIServiceSignal(aiService: string) {
this.toggleAIService.next(aiService);
}

setSelectedConversationId(conversationId: object) {
this.selectedConversationIdSubject.next(conversationId);
}
Expand Down
Loading