reorganize dirs for dockerize

This commit is contained in:
Danny Avila 2023-03-06 10:15:07 -05:00
parent 6ae154cc42
commit fca546af63
38 changed files with 1056 additions and 1970 deletions

12
api/DockerFile Normal file
View file

@ -0,0 +1,12 @@
FROM node:latest
WORKDIR /api
# copy package.json into the container at /api
COPY package*.json /api/
# install dependencies
RUN npm install
# Copy the current directory contents into the container at /api
COPY . /api/
# Make port 80 available to the world outside this container
EXPOSE 80
# Run the app when the container launches
CMD ["npm", "start"]

55
api/app/bingai.js Normal file
View file

@ -0,0 +1,55 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
const askBing = async ({ text, progressCallback, convo }) => {
const { BingAIClient } = (await import('@waylaidwanderer/chatgpt-api'));
const bingAIClient = new BingAIClient({
// "_U" cookie from bing.com
userToken: process.env.BING_TOKEN,
// If the above doesn't work, provide all your cookies as a string instead
// cookies: '',
debug: false,
store: new KeyvFile({ filename: './api/data/cache.json' })
});
let options = {
onProgress: async (partialRes) => await progressCallback(partialRes),
};
if (convo) {
options = { ...options, ...convo };
}
const res = await bingAIClient.sendMessage(text, options
);
return res;
// Example response for reference
// {
// conversationSignature: 'wwZ2GC/qRgEqP3VSNIhbPGwtno5RcuBhzZFASOM+Sxg=',
// conversationId: '51D|BingProd|026D3A4017554DE6C446798144B6337F4D47D5B76E62A31F31D0B1D0A95ED868',
// clientId: '914800201536527',
// invocationId: 1,
// conversationExpiryTime: '2023-02-15T21:48:46.2892088Z',
// response: 'Hello, this is Bing. Nice to meet you. 😊',
// details: {
// text: 'Hello, this is Bing. Nice to meet you. 😊',
// author: 'bot',
// createdAt: '2023-02-15T15:48:43.0631898+00:00',
// timestamp: '2023-02-15T15:48:43.0631898+00:00',
// messageId: '9d0c9a80-91b1-49ab-b9b1-b457dc3fe247',
// requestId: '5b252ef8-4f09-4c08-b6f5-4499d2e12fba',
// offense: 'None',
// adaptiveCards: [ [Object] ],
// sourceAttributions: [],
// feedback: { tag: null, updatedOn: null, type: 'None' },
// contentOrigin: 'DeepLeo',
// privacy: null,
// suggestedResponses: [ [Object], [Object], [Object] ]
// }
// }
};
module.exports = { askBing };

View file

@ -0,0 +1,32 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
const clientOptions = {
// Warning: This will expose your access token to a third party. Consider the risks before using this.
reverseProxyUrl: 'https://chatgpt.duti.tech/api/conversation',
// Access token from https://chat.openai.com/api/auth/session
accessToken: process.env.CHATGPT_TOKEN
};
const browserClient = async ({ text, progressCallback, convo }) => {
const { ChatGPTBrowserClient } = await import('@waylaidwanderer/chatgpt-api');
const store = {
store: new KeyvFile({ filename: './api/data/cache.json' })
};
const client = new ChatGPTBrowserClient(clientOptions, store);
let options = {
onProgress: async (partialRes) => await progressCallback(partialRes)
};
if (!!convo.parentMessageId && !!convo.conversationId) {
options = { ...options, ...convo };
}
const res = await client.sendMessage(text, options);
return res;
};
module.exports = { browserClient };

31
api/app/chatgpt-client.js Normal file
View file

@ -0,0 +1,31 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
const clientOptions = {
modelOptions: {
model: 'gpt-3.5-turbo'
},
debug: false
};
const askClient = async ({ text, progressCallback, convo }) => {
const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default;
const store = {
store: new KeyvFile({ filename: './api/data/cache.json' })
};
const client = new ChatGPTClient(process.env.OPENAI_KEY, clientOptions, store);
let options = {
onProgress: async (partialRes) => await progressCallback(partialRes)
};
if (!!convo.parentMessageId && !!convo.conversationId) {
options = { ...options, ...convo };
}
const res = await client.sendMessage(text, options);
return res;
};
module.exports = { askClient };

37
api/app/chatgpt-custom.js Normal file
View file

@ -0,0 +1,37 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
const clientOptions = {
modelOptions: {
model: 'gpt-3.5-turbo'
},
debug: false
};
const customClient = async ({ text, progressCallback, convo, promptPrefix, chatGptLabel }) => {
const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default;
const store = {
store: new KeyvFile({ filename: './api/data/cache.json' })
};
clientOptions.chatGptLabel = chatGptLabel;
if (promptPrefix.length > 0) {
clientOptions.promptPrefix = promptPrefix;
}
const client = new ChatGPTClient(process.env.OPENAI_KEY, clientOptions, store);
let options = {
onProgress: async (partialRes) => await progressCallback(partialRes)
};
if (!!convo.parentMessageId && !!convo.conversationId) {
options = { ...options, ...convo };
}
const res = await client.sendMessage(text, options);
return res;
};
module.exports = customClient;

38
api/app/chatgpt.js Normal file
View file

@ -0,0 +1,38 @@
require('dotenv').config();
const Keyv = require('keyv');
const { Configuration, OpenAIApi } = require('openai');
const messageStore = new Keyv(process.env.MONGODB_URI, { namespace: 'chatgpt' });
const ask = async (question, progressCallback, convo) => {
const { ChatGPTAPI } = await import('chatgpt');
const api = new ChatGPTAPI({ apiKey: process.env.OPENAI_KEY, messageStore });
let options = {
onProgress: async (partialRes) => {
if (partialRes.text.length > 0) {
await progressCallback(partialRes);
}
}
};
if (!!convo.parentMessageId && !!convo.conversationId) {
options = { ...options, ...convo };
}
const res = await api.sendMessage(question, options);
return res;
};
const titleConvo = async (message, response, model) => {
const configuration = new Configuration({
apiKey: process.env.OPENAI_KEY
});
const openai = new OpenAIApi(configuration);
const completion = await openai.createCompletion({
model: 'text-davinci-002',
prompt: `Write a short title in title case, ideally in 5 words or less, and do not refer to the user or ${model}, that summarizes this conversation:\nUser:"${message}"\n${model}:"${response}"\nTitle: `
});
return completion.data.choices[0].text.replace(/\n/g, '');
};
module.exports = { ask, titleConvo };

54
api/app/detectCode.js Normal file
View file

@ -0,0 +1,54 @@
const { ModelOperations } = require('@vscode/vscode-languagedetection');
const codeRegex = /(```[\s\S]*?```)/g;
const languageMatch = /```(\w+)/;
const detectCode = async (text) => {
try {
if (!text.match(codeRegex)) {
// console.log('disqualified for non-code match')
return text;
}
if (text.match(languageMatch)) {
// console.log('disqualified for language match')
return text;
}
// console.log('qualified for code match');
const modelOperations = new ModelOperations();
const regexSplit = (await import('../src/utils/regexSplit.mjs')).default;
const parts = regexSplit(text, codeRegex);
const output = parts.map(async (part, i) => {
if (part.match(codeRegex)) {
const code = part.slice(3, -3);
const language = await modelOperations.runModel(code);
return part.replace(/^```/, `\`\`\`${language[0].languageId}`);
} else {
// return i > 0 ? '\n' + part : part;
return part;
}
});
return (await Promise.all(output)).join('');
} catch (e) {
console.log('Error in detectCode function\n', e);
return text;
}
};
const example3 = {
text: "By default, the function generates an 8-character password with uppercase and lowercase letters and digits, but no special characters.\n\nTo use this function, simply call it with the desired arguments. For example:\n\n```\n>>> generate_password()\n'wE5pUxV7'\n>>> generate_password(length=12, special_chars=True)\n'M4v&^gJ*8#qH'\n>>> generate_password(uppercase=False, digits=False)\n'zajyprxr'\n``` \n\nNote that the randomness is used to select characters from the available character sets, but the resulting password is always deterministic given the same inputs. This makes the function useful for generating secure passwords that meet specific requirements."
};
const example4 = {
text: 'here\'s a cool function:\n```\nimport random\nimport string\n\ndef generate_password(length=8, uppercase=True, lowercase=True, digits=True, special_chars=False):\n """Generate a random password with specified requirements.\n\n Args:\n length (int): The length of the password. Default is 8.\n uppercase (bool): Whether to include uppercase letters. Default is True.\n lowercase (bool): Whether to include lowercase letters. Default is True.\n digits (bool): Whether to include digits. Default is True.\n special_chars (bool): Whether to include special characters. Default is False.\n\n Returns:\n str: A random password with the specified requirements.\n """\n # Define character sets to use in password generation\n chars = ""\n if uppercase:\n chars += string.ascii_uppercase\n if lowercase:\n chars += string.ascii_lowercase\n if digits:\n chars += string.digits\n if special_chars:\n chars += string.punctuation\n\n # Generate the password\n password = "".join(random.choice(chars) for _ in range(length))\n return password\n```\n\nThis function takes several arguments'
};
// write an immediately invoked function to test this
// (async () => {
// const result = await detectCode(example3.text);
// console.log(result);
// })();
module.exports = detectCode;

15
api/app/index.js Normal file
View file

@ -0,0 +1,15 @@
const { askClient } = require('./chatgpt-client');
const { browserClient } = require('./chatgpt-browser');
const customClient = require('./chatgpt-custom');
const { askBing } = require('./bingai');
const titleConvo = require('./titleConvo');
const detectCode = require('./detectCode');
module.exports = {
askClient,
browserClient,
customClient,
askBing,
titleConvo,
detectCode
};

24
api/app/titleConvo.js Normal file
View file

@ -0,0 +1,24 @@
const { Configuration, OpenAIApi } = require('openai');
const titleConvo = async ({ message, response, model }) => {
const configuration = new Configuration({
apiKey: process.env.OPENAI_KEY
});
const openai = new OpenAIApi(configuration);
const completion = await openai.createChatCompletion({
model: 'gpt-3.5-turbo',
messages: [
{
role: 'system',
content:
'You are a title-generator with one job: titling the conversation provided by a user in title case.'
},
{ role: 'user', content: `In 5 words or less, summarize the conversation below with a title in title case. Don't refer to the participants of the conversation by name. Do not include punctuation or quotation marks. Your response should be in title case, exclusively containing the title. Conversation:\n\nUser: "${message}"\n\n${model}: "${response}"\n\nTitle: ` },
]
});
//eslint-disable-next-line
return completion.data.choices[0].message.content.replace(/["\.]/g, '');
};
module.exports = titleConvo;

View file

@ -0,0 +1,95 @@
const mongoose = require('mongoose');
const { getMessages, deleteMessages } = require('./Message');
const convoSchema = mongoose.Schema({
conversationId: {
type: String,
unique: true,
required: true
},
parentMessageId: {
type: String,
required: true
},
title: {
type: String,
default: 'New conversation'
},
conversationSignature: {
type: String
},
clientId: {
type: String
},
invocationId: {
type: String
},
chatGptLabel: {
type: String
},
promptPrefix: {
type: String
},
model: {
type: String
},
suggestions: [{ type: String }],
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
created: {
type: Date,
default: Date.now
}
});
const Conversation =
mongoose.models.Conversation || mongoose.model('Conversation', convoSchema);
module.exports = {
saveConvo: async ({ conversationId, title, ...convo }) => {
try {
const messages = await getMessages({ conversationId });
const update = { ...convo, messages };
if (title) {
update.title = title;
}
return await Conversation.findOneAndUpdate(
{ conversationId },
{ $set: update },
{ new: true, upsert: true }
).exec();
} catch (error) {
console.log(error);
return { message: 'Error saving conversation' };
}
},
updateConvo: async ({ conversationId, ...update }) => {
try {
return await Conversation.findOneAndUpdate({ conversationId }, update, {
new: true
}).exec();
} catch (error) {
console.log(error);
return { message: 'Error updating conversation' };
}
},
// getConvos: async () => await Conversation.find({}).sort({ created: -1 }).exec(),
getConvos: async (pageNumber = 1, pageSize = 12) => {
const skip = (pageNumber - 1) * pageSize;
// const limit = pageNumber * pageSize;
const conversations = await Conversation.find({})
.sort({ created: -1 })
.skip(skip)
// .limit(limit)
.limit(pageSize)
.exec();
return conversations;
},
deleteConvos: async (filter) => {
let deleteCount = await Conversation.deleteMany(filter).exec();
deleteCount.messages = await deleteMessages(filter);
return deleteCount;
}
};

73
api/models/CustomGpt.js Normal file
View file

@ -0,0 +1,73 @@
const mongoose = require('mongoose');
const customGptSchema = mongoose.Schema({
chatGptLabel: {
type: String,
required: true
},
promptPrefix: {
type: String
},
value: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
}
});
const CustomGpt = mongoose.models.CustomGpt || mongoose.model('CustomGpt', customGptSchema);
const createCustomGpt = async ({ chatGptLabel, promptPrefix, value }) => {
try {
await CustomGpt.create({
chatGptLabel,
promptPrefix,
value
});
return { chatGptLabel, promptPrefix, value };
} catch (error) {
console.error(error);
return { customGpt: 'Error saving customGpt' };
}
};
module.exports = {
getCustomGpts: async (filter) => {
try {
return await CustomGpt.find(filter).exec();
} catch (error) {
console.error(error);
return { customGpt: 'Error getting customGpts' };
}
},
updateCustomGpt: async ({ value, ...update }) => {
try {
console.log('updateCustomGpt', value, update);
const customGpt = await CustomGpt.findOne({ value }).exec();
if (!customGpt) {
return await createCustomGpt({ value, ...update });
} else {
return await CustomGpt.findOneAndUpdate({ value }, update, {
new: true,
upsert: true
}).exec();
}
} catch (error) {
console.log(error);
return { message: 'Error updating customGpt' };
}
},
deleteCustomGpts: async (filter) => {
try {
return await CustomGpt.deleteMany(filter).exec();
} catch (error) {
console.error(error);
return { customGpt: 'Error deleting customGpts' };
}
}
};

75
api/models/Message.js Normal file
View file

@ -0,0 +1,75 @@
const mongoose = require('mongoose');
const messageSchema = mongoose.Schema({
id: {
type: String,
unique: true,
required: true
},
conversationId: {
type: String,
required: true
},
conversationSignature: {
type: String,
// required: true
},
clientId: {
type: String,
},
invocationId: {
type: String,
},
parentMessageId: {
type: String,
// required: true
},
sender: {
type: String,
required: true
},
text: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
}
});
const Message = mongoose.models.Message || mongoose.model('Message', messageSchema);
module.exports = {
saveMessage: async ({ id, conversationId, parentMessageId, sender, text }) => {
try {
await Message.create({
id,
conversationId,
parentMessageId,
sender,
text
});
return { id, conversationId, parentMessageId, sender, text };
} catch (error) {
console.error(error);
return { message: 'Error saving message' };
}
},
getMessages: async (filter) => {
try {
return await Message.find(filter).exec()
} catch (error) {
console.error(error);
return { message: 'Error getting messages' };
}
},
deleteMessages: async (filter) => {
try {
return await Message.deleteMany(filter).exec()
} catch (error) {
console.error(error);
return { message: 'Error deleting messages' };
}
}
}

52
api/models/Prompt.js Normal file
View file

@ -0,0 +1,52 @@
const mongoose = require('mongoose');
const promptSchema = mongoose.Schema({
title: {
type: String,
required: true
},
prompt: {
type: String,
required: true
},
category: {
type: String,
},
created: {
type: Date,
default: Date.now
}
});
const Prompt = mongoose.models.Prompt || mongoose.model('Prompt', promptSchema);
module.exports = {
savePrompt: async ({ title, prompt }) => {
try {
await Prompt.create({
title,
prompt
});
return { title, prompt };
} catch (error) {
console.error(error);
return { prompt: 'Error saving prompt' };
}
},
getPrompts: async (filter) => {
try {
return await Prompt.find(filter).exec()
} catch (error) {
console.error(error);
return { prompt: 'Error getting prompts' };
}
},
deletePrompts: async (filter) => {
try {
return await Prompt.deleteMany(filter).exec()
} catch (error) {
console.error(error);
return { prompt: 'Error deleting prompts' };
}
}
}

44
api/models/dbConnect.js Normal file
View file

@ -0,0 +1,44 @@
require('dotenv').config();
const mongoose = require('mongoose');
const MONGO_URI = process.env.MONGO_URI;
if (!MONGO_URI) {
throw new Error('Please define the MONGO_URI environment variable inside .env.local');
}
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections growing exponentially
* during API Route usage.
*/
let cached = global.mongoose;
if (!cached) {
cached = global.mongoose = { conn: null, promise: null };
}
async function dbConnect() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
bufferCommands: false
// bufferMaxEntries: 0,
// useFindAndModify: true,
// useCreateIndex: true
};
mongoose.set('strictQuery', true);
cached.promise = mongoose.connect(MONGO_URI, opts).then((mongoose) => {
return mongoose;
});
}
cached.conn = await cached.promise;
return cached.conn;
}
module.exports = dbConnect;

12
api/models/index.js Normal file
View file

@ -0,0 +1,12 @@
const { saveMessage, deleteMessages } = require('./Message');
const { getCustomGpts, updateCustomGpt, deleteCustomGpts } = require('./CustomGpt');
const { saveConvo } = require('./Conversation');
module.exports = {
saveMessage,
deleteMessages,
saveConvo,
getCustomGpts,
updateCustomGpt,
deleteCustomGpts
};

5
api/nodemon.json Normal file
View file

@ -0,0 +1,5 @@
{
"ignore": [
"data/"
]
}

38
api/package.json Normal file
View file

@ -0,0 +1,38 @@
{
"name": "chatgpt-clone",
"version": "1.0.0",
"description": "",
"main": "server/index.js",
"scripts": {
"start": "npx node server/index.js",
"server-dev": "npx nodemon server/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/danny-avila/chatgpt-clone.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/danny-avila/chatgpt-clone/issues"
},
"homepage": "https://github.com/danny-avila/chatgpt-clone#readme",
"dependencies": {
"@keyv/mongo": "^2.1.8",
"@waylaidwanderer/chatgpt-api": "^1.15.1",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
"mongoose": "^6.9.0",
"openai": "^3.1.0",
"swr": "^2.0.3",
"tailwind-merge": "^1.9.1",
"tailwindcss-animate": "^1.0.5"
},
"devDependencies": {
"nodemon": "^2.0.20",
"path": "^0.12.7"
}
}

28
api/server/index.js Normal file
View file

@ -0,0 +1,28 @@
const express = require('express');
const dbConnect = require('../models/dbConnect');
const path = require('path');
const cors = require('cors');
const routes = require('./routes');
const app = express();
const port = process.env.PORT || 3050;
const projectPath = path.join(__dirname, '..', '..');
dbConnect().then(() => console.log('Connected to MongoDB'));
app.use(cors());
app.use(express.json());
app.use(express.static(path.join(projectPath, 'public')));
app.get('/', function (req, res) {
console.log(path.join(projectPath, 'public', 'index.html'));
res.sendFile(path.join(projectPath, 'public', 'index.html'));
});
app.use('/ask', routes.ask);
app.use('/messages', routes.messages);
app.use('/convos', routes.convos);
app.use('/customGpts', routes.customGpts);
app.use('/prompts', routes.prompts);
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});

143
api/server/routes/ask.js Normal file
View file

@ -0,0 +1,143 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const askBing = require('./askBing');
const {
titleConvo,
askClient,
browserClient,
customClient,
detectCode
} = require('../../app/');
const { saveMessage, deleteMessages, saveConvo } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
router.use('/bing', askBing);
router.post('/', async (req, res) => {
const { model, text, parentMessageId, conversationId, chatGptLabel, promptPrefix } =
req.body;
if (!text.trim().includes(' ') && text.length < 5) {
return handleError(res, 'Prompt empty or too short');
}
const userMessageId = crypto.randomUUID();
let userMessage = { id: userMessageId, sender: 'User', text };
console.log('ask log', {
model,
...userMessage,
parentMessageId,
conversationId,
chatGptLabel,
promptPrefix
});
let client;
if (model === 'chatgpt') {
client = askClient;
} else if (model === 'chatgptCustom') {
client = customClient;
} else {
client = browserClient;
}
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Access-Control-Allow-Origin': '*',
'X-Accel-Buffering': 'no'
});
try {
let i = 0;
let tokens = '';
const progressCallback = async (partial) => {
if (i === 0 && typeof partial === 'object') {
userMessage.parentMessageId = parentMessageId ? parentMessageId : partial.id;
userMessage.conversationId = conversationId ? conversationId : partial.conversationId;
await saveMessage(userMessage);
sendMessage(res, { ...partial, initial: true });
i++;
}
if (typeof partial === 'object') {
sendMessage(res, { ...partial, message: true });
} else {
tokens += partial === text ? '' : partial;
if (tokens.includes('[DONE]')) {
tokens = tokens.replace('[DONE]', '');
}
// tokens = await detectCode(tokens);
sendMessage(res, { text: tokens, message: true, initial: i === 0 ? true : false });
i++;
}
};
let gptResponse = await client({
text,
progressCallback,
convo: {
parentMessageId,
conversationId
},
chatGptLabel,
promptPrefix
});
console.log('CLIENT RESPONSE', gptResponse);
if (!gptResponse.parentMessageId) {
gptResponse.text = gptResponse.response;
gptResponse.id = gptResponse.messageId;
gptResponse.parentMessageId = gptResponse.messageId;
userMessage.parentMessageId = parentMessageId ? parentMessageId : gptResponse.messageId;
userMessage.conversationId = conversationId
? conversationId
: gptResponse.conversationId;
await saveMessage(userMessage);
delete gptResponse.response;
}
if (
(gptResponse.text.includes('2023') && !gptResponse.text.trim().includes(' ')) ||
gptResponse.text.toLowerCase().includes('no response') ||
gptResponse.text.toLowerCase().includes('no answer')
) {
return handleError(res, 'Prompt empty or too short');
}
if (!parentMessageId) {
gptResponse.title = await titleConvo({
model,
message: text,
response: JSON.stringify(gptResponse.text)
});
}
gptResponse.sender = model === 'chatgptCustom' ? chatGptLabel : model;
gptResponse.final = true;
gptResponse.text = await detectCode(gptResponse.text);
if (chatGptLabel?.length > 0 && model === 'chatgptCustom') {
gptResponse.chatGptLabel = chatGptLabel;
}
if (promptPrefix?.length > 0 && model === 'chatgptCustom') {
gptResponse.promptPrefix = promptPrefix;
}
await saveMessage(gptResponse);
await saveConvo(gptResponse);
sendMessage(res, gptResponse);
res.end();
} catch (error) {
console.log(error);
await deleteMessages({ id: userMessageId });
handleError(res, error.message);
}
});
module.exports = router;

View file

@ -0,0 +1,72 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const { titleConvo, askBing } = require('../../app/');
const { saveMessage, deleteMessages, saveConvo } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
router.post('/', async (req, res) => {
const { model, text, ...convo } = req.body;
if (!text.trim().includes(' ') && text.length < 5) {
return handleError(res, 'Prompt empty or too short');
}
const userMessageId = crypto.randomUUID();
let userMessage = { id: userMessageId, sender: 'User', text };
console.log('ask log', { model, ...userMessage, ...convo });
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Access-Control-Allow-Origin': '*',
'X-Accel-Buffering': 'no'
});
try {
let tokens = '';
const progressCallback = async (partial) => {
tokens += partial === text ? '' : partial;
// tokens = appendCode(tokens);
sendMessage(res, { text: tokens, message: true });
};
let response = await askBing({
text,
progressCallback,
convo
});
console.log('CLIENT RESPONSE');
console.dir(response, { depth: null });
userMessage.conversationSignature =
convo.conversationSignature || response.conversationSignature;
userMessage.conversationId = convo.conversationId || response.conversationId;
userMessage.invocationId = response.invocationId;
await saveMessage(userMessage);
if (!convo.conversationSignature) {
response.title = await titleConvo(text, response.response, model);
}
response.text = response.response;
response.id = response.details.messageId;
response.suggestions =
response.details.suggestedResponses &&
response.details.suggestedResponses.map((s) => s.text);
response.sender = model;
response.final = true;
await saveMessage(response);
await saveConvo(response);
sendMessage(res, response);
res.end();
} catch (error) {
console.log(error);
await deleteMessages({ id: userMessageId });
handleError(res, error.message);
}
});
module.exports = router;

View file

@ -0,0 +1,38 @@
const express = require('express');
const router = express.Router();
const { getConvos, deleteConvos, updateConvo } = require('../../models/Conversation');
router.get('/', async (req, res) => {
const pageNumber = req.query.pageNumber || 1;
res.status(200).send(await getConvos(pageNumber));
});
router.post('/clear', async (req, res) => {
let filter = {};
const { conversationId } = req.body.arg;
if (conversationId) {
filter = { conversationId };
}
try {
const dbResponse = await deleteConvos(filter);
res.status(201).send(dbResponse);
} catch (error) {
console.error(error);
res.status(500).send(error);
}
});
router.post('/update', async (req, res) => {
const update = req.body.arg;
try {
const dbResponse = await updateConvo(update);
res.status(201).send(dbResponse);
} catch (error) {
console.error(error);
res.status(500).send(error);
}
});
module.exports = router;

View file

@ -0,0 +1,56 @@
const express = require('express');
const router = express.Router();
const { getCustomGpts, updateCustomGpt, deleteCustomGpts } = require('../../models');
router.get('/', async (req, res) => {
const models = (await getCustomGpts()).map(model => {
model = model.toObject();
model._id = model._id.toString();
return model;
});
// console.log(models);
res.status(200).send(models);
});
router.post('/delete/:_id', async (req, res) => {
const { _id } = req.params;
let filter = {};
if (_id) {
filter = { _id };
}
try {
const dbResponse = await deleteCustomGpts(filter);
res.status(201).send(dbResponse);
} catch (error) {
console.error(error);
res.status(500).send(error);
}
});
// router.post('/create', async (req, res) => {
// const payload = req.body.arg;
// try {
// const dbResponse = await createCustomGpt(payload);
// res.status(201).send(dbResponse);
// } catch (error) {
// console.error(error);
// res.status(500).send(error);
// }
// });
router.post('/', async (req, res) => {
const update = req.body.arg;
try {
const dbResponse = await updateCustomGpt(update);
res.status(201).send(dbResponse);
} catch (error) {
console.error(error);
res.status(500).send(error);
}
});
module.exports = router;

View file

@ -0,0 +1,13 @@
const handleError = (res, errorMessage) => {
res.status(500).write(`event: error\ndata: ${errorMessage}`);
res.end();
};
const sendMessage = (res, message) => {
if (message.length === 0) {
return;
}
res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
};
module.exports = { handleError, sendMessage };

View file

@ -0,0 +1,7 @@
const ask = require('./ask');
const messages = require('./messages');
const convos = require('./convos');
const customGpts = require('./customGpts');
const prompts = require('./prompts');
module.exports = { ask, messages, convos, customGpts, prompts };

View file

@ -0,0 +1,10 @@
const express = require('express');
const router = express.Router();
const { getMessages } = require('../../models/Message');
router.get('/:conversationId', async (req, res) => {
const { conversationId } = req.params;
res.status(200).send(await getMessages({ conversationId }));
});
module.exports = router;

View file

@ -0,0 +1,14 @@
const express = require('express');
const router = express.Router();
const { savePrompt, getPrompts, deletePrompts } = require('../../models/Prompt');
router.get('/', async (req, res) => {
let filter = {};
// const { search } = req.body.arg;
// if (!!search) {
// filter = { conversationId };
// }
res.status(200).send(await getPrompts(filter));
});
module.exports = router;