Skip to content
Draft
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
11 changes: 6 additions & 5 deletions chatapi/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
import express from 'express';
import dotenv from 'dotenv';
import cors from 'cors';
Expand Down Expand Up @@ -43,7 +44,7 @@ wss.on('connection', (ws) => {
if (chatResponse) {
ws.send(JSON.stringify({
'type': 'final',
'completionText': chatResponse.completionText,
'completionText': chatResponse.completion,
'couchDBResponse': chatResponse.couchSaveResponse
}));
}
Expand All @@ -53,7 +54,7 @@ wss.on('connection', (ws) => {
});

ws.on('close', () => {
console.log('WebSocket connection closed'); // eslint-disable-line no-console
console.log('WebSocket connection closed');
});
});

Expand All @@ -66,7 +67,7 @@ app.post('/', async (req: any, res: any) => {

try {
if (!save) {
const response = await chatNoSave(data.content, data.aiProvider, data.context, data.assistant, false);
const response = await chatNoSave(data.content, data.aiProvider, data.context, false);
return res.status(200).json({
'status': 'Success',
'chat': response
Expand All @@ -75,7 +76,7 @@ app.post('/', async (req: any, res: any) => {
const response = await chat(data, false);
return res.status(201).json({
'status': 'Success',
'chat': response?.completionText,
'chat': response?.completion,
'couchDBResponse': response?.couchSaveResponse
});
}
Expand All @@ -95,4 +96,4 @@ app.get('/checkproviders', async (req: any, res: any) => {

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

server.listen(port, () => console.log(`Server running on port ${port}`)); // eslint-disable-line no-console
server.listen(port, () => console.log(`Server running on port ${port}`));
6 changes: 5 additions & 1 deletion chatapi/src/models/chat.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface ModelsDocument {
}

export interface ChatMessage {
role: 'user' | 'assistant';
role: 'user' | 'assistant' | 'developer';
content: string;
}

Expand All @@ -34,3 +34,7 @@ export interface ChatItem {
response: string;
}

export interface ChatResponse {
responseId: string;
message: string;
}
30 changes: 20 additions & 10 deletions chatapi/src/services/chat.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { DocumentInsertResponse } from 'nano';

import { assistant } from '../config/ai-providers.config';
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, ChatResponse } from '../models/chat.model';

function handleChatError(error: any) {
if (error.response) {
throw new Error(`GPT Service Error: ${error.response.status} - ${error.response.data?.error?.code}`);
throw new Error(`Chatapi Service Error: ${error.response.status} - ${error.response.data?.error?.code}`);
} else {
throw new Error(error.message);
}
Expand All @@ -21,7 +22,7 @@ function handleChatError(error: any) {
* @returns Object with completion text and CouchDB save response
*/
export async function chat(data: any, stream?: boolean, callback?: (response: string) => void): Promise<{
completionText: string;
completion: string | ChatResponse;
couchSaveResponse: DocumentInsertResponse;
} | undefined> {
const { content, ...dbData } = data;
Expand All @@ -39,15 +40,24 @@ export async function chat(data: any, stream?: boolean, callback?: (response: st
dbData.conversations = [];
dbData.createdDate = Date.now();
dbData.aiProvider = aiProvider.name;
messages.push({
'role': 'developer',
'content': assistant.instructions || ''
});
}

dbData.conversations.push({ 'id': Date.now().toString(), 'query': content, 'response': '' });
dbData.conversations.push({
'id': Date.now().toString(),
'query': content,
'response': ''
});
const res = await chatDB.insert(dbData);

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

try {
const completionText = await aiChat(messages, aiProvider, dbData.assistant, dbData.context, stream, callback);
const completion = await aiChat(messages, aiProvider, dbData.context, stream, callback);
const completionText = typeof completion === 'string' ? completion : completion.message;

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

Expand All @@ -57,7 +67,7 @@ export async function chat(data: any, stream?: boolean, callback?: (response: st
const couchSaveResponse = await chatDB.insert(dbData);

return {
completionText,
completion,
couchSaveResponse
};
} catch (error: any) {
Expand All @@ -68,22 +78,22 @@ export async function chat(data: any, stream?: boolean, callback?: (response: st
export async function chatNoSave(
content: any,
aiProvider: AIProvider,
assistant: boolean,
context?: any,
stream?: boolean,
callback?: (response: string) => void
): Promise<string | undefined> {
): Promise<ChatResponse | string | undefined> {
const messages: ChatMessage[] = [];

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

try {
const completionText = await aiChat(messages, aiProvider, assistant, context, stream, callback);
const completion = await aiChat(messages, aiProvider, context, stream, callback);
const completionText = typeof completion === 'string' ? completion : completion.message;
messages.push({
'role': 'assistant', 'content': completionText
});

return completionText;
return completion;
} catch (error: any) {
handleChatError(error);
}
Expand Down
103 changes: 0 additions & 103 deletions chatapi/src/utils/chat-assistant.utils.ts

This file was deleted.

72 changes: 17 additions & 55 deletions chatapi/src/utils/chat-helpers.utils.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
import { models } from '../config/ai-providers.config';
import { AIProvider, ChatMessage } from '../models/chat.model';
import { AIProvider, ChatMessage, ChatResponse } from '../models/chat.model';
import { Attachment } from '../models/db-doc.model';
import { fetchFileFromCouchDB } from './db.utils';
import {
createAssistant,
createThread,
addToThread,
createRun,
waitForRunCompletion,
retrieveResponse,
createAndHandleRunWithStreaming,
} from './chat-assistant.utils';
import { extractTextFromDocument } from './text-extraction.utils';

/**
Expand All @@ -22,8 +13,7 @@ import { extractTextFromDocument } from './text-extraction.utils';
export async function aiChatStream(
messages: ChatMessage[],
aiProvider: AIProvider,
assistant: boolean,
context: any = '',
context: any,
callback?: (response: string) => void
): Promise<string> {
const provider = models[aiProvider.name];
Expand All @@ -32,30 +22,15 @@ export async function aiChatStream(
}
const model = aiProvider.model ?? provider.defaultModel;

if (assistant) {
try {
const asst = await createAssistant(model);
const thread = await createThread();
for (const message of messages) {
await addToThread(thread.id, message.content);
}

const completionText = await createAndHandleRunWithStreaming(thread.id, asst.id, context.data, callback);

return completionText;
} catch (error) {
throw new Error(`Error processing request ${error}`);
}
}

const completion = await provider.ai.chat.completions.create({
const stream = await provider.ai.responses.create({
model,
messages,
'instructions': context.data || '',
'input': messages,
'stream': true,
});

let completionText = '';
for await (const chunk of completion) {
for await (const chunk of stream) {
if (chunk.choices && chunk.choices.length > 0) {
const response = chunk.choices[0].delta?.content || '';
completionText += response;
Expand All @@ -78,9 +53,8 @@ export async function aiChatStream(
export async function aiChatNonStream(
messages: ChatMessage[],
aiProvider: AIProvider,
assistant: boolean,
context: any = '',
): Promise<string> {
context: any,
): Promise<ChatResponse> {
const provider = models[aiProvider.name];
if (!provider) {
throw new Error('Unsupported AI provider');
Expand All @@ -100,31 +74,19 @@ export async function aiChatNonStream(
}
}

if (assistant) {
try {
const asst = await createAssistant(model);
const thread = await createThread();
for (const message of messages) {
await addToThread(thread.id, message.content);
}
const run = await createRun(thread.id, asst.id, context.data);
await waitForRunCompletion(thread.id, run.id);

return await retrieveResponse(thread.id);
} catch (error) {
throw new Error(`Error processing request ${error}`);
}
}

const completion = await provider.ai.chat.completions.create({
const response = await provider.ai.responses.create({
model,
messages,
'instructions': context.data || '',
'input': messages,
});

const completionText = completion.choices[0]?.message?.content;
if (!completionText) {
const responseText = response.output_text;
if (!responseText) {
throw new Error('Unexpected API response');
}

return completionText;
return {
'responseId': response.id,
'message': response.output_text
};
}
9 changes: 4 additions & 5 deletions chatapi/src/utils/chat.utils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { aiChatStream, aiChatNonStream } from './chat-helpers.utils';
import { AIProvider, ChatMessage } from '../models/chat.model';
import { AIProvider, ChatMessage, ChatResponse } from '../models/chat.model';

export async function aiChat(
messages: ChatMessage[],
aiProvider: AIProvider,
assistant: boolean,
context?: any,
stream?: boolean,
callback?: (response: string) => void
): Promise<string> {
): Promise<ChatResponse | string> {
if (stream) {
return await aiChatStream(messages, aiProvider, assistant, context, callback);
return await aiChatStream(messages, aiProvider, context, callback);
} else {
return await aiChatNonStream(messages, aiProvider, assistant, context);
return await aiChatNonStream(messages, aiProvider, context);
}
}
Loading
Loading