Skip to content

Commit

Permalink
Merge pull request #16 from danny-avila/updateAI
Browse files Browse the repository at this point in the history
feat: add sydney (jailbroken bing) and more bing styling with citations
  • Loading branch information
danny-avila authored Mar 10, 2023
2 parents 732c75d + 4383894 commit 12cf340
Show file tree
Hide file tree
Showing 28 changed files with 377 additions and 88 deletions.
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,23 @@ https://user-images.githubusercontent.com/110412045/223754183-8b7f45ce-6517-4bd5

## Updates
<details open>
<summary><strong>2023-03-09</strong></summary>
Released v.0.0.2

Adds Sydney (jailbroken Bing AI) to the model menu. Thank you [DavesDevFails](https://github.com/DavesDevFails) for bringing it to my attention in this [issue](https://github.com/danny-avila/chatgpt-clone/issues/13). Bing/Sydney now correctly cite links, more styling to come. Fix some overlooked bugs, and model menu doesn't close upon deleting a customGpt.


I've re-enabled the ChatGPT browser client (free version) since it might be working for most people, it no longer works for me. Sydney is the best free route anyway.
</details>

<details>
<details>
<summary><strong>2023-03-07</strong></summary>
Due to increased interest in the repo, I've dockerized the app as of this update for quick setup! See setup instructions below. I realize this still takes some time with installing docker dependencies, so it's on the roadmap to have a deployed demo. Besides this, I've made major improvements for a lot of the existing features across the board, mainly UI/UX.


Also worth noting, the method to access the Free Version is no longer working, so I've removed it from model selection until further notice.
</details>

<details>
<summary><strong>Previous Updates</strong></summary>

<details>
Expand Down Expand Up @@ -80,12 +89,12 @@ Here are my recently completed and planned features:
- [x] Customize prompt prefix/label (custom ChatGPT using official API)
- [x] Server convo pagination (limit fetch and load more with 'show more' button)
- [x] Config file for easy startup (docker compose)
- [ ] Bing AI Styling (for suggested responses, convo end, etc.) - **In progress**
- [ ] Add warning before clearing convos
- [ ] Build test suite for CI/CD
- [ ] Conversation Search (by title)
- [ ] Resubmit/edit sent messages
- [ ] Semantic Search Option (requires more tokens)
- [ ] Bing AI Styling (for suggested responses, convo end, etc.)
- [ ] Prompt Templates/Search
- [ ] Refactor/clean up code (tech debt)
- [ ] Optional use of local storage for credentials
Expand Down Expand Up @@ -172,7 +181,7 @@ The Bing Access Token is the "_U" cookie from bing.com. Use dev tools or an exte
</details>

### Updating
- As the project is still a work-in-progress, you should pull the latest and run some of the steps above again
- As the project is still a work-in-progress, you should pull the latest and run the steps over. Reset your browser cache/clear site data.

## Use Cases ##

Expand Down
2 changes: 1 addition & 1 deletion api/app/bingai.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const askBing = async ({ text, progressCallback, convo }) => {
// If the above doesn't work, provide all your cookies as a string instead
// cookies: '',
debug: false,
store: new KeyvFile({ filename: './data/cache.json' })
cache: { store: new KeyvFile({ filename: './data/cache.json' }) }
});

let options = {
Expand Down
3 changes: 2 additions & 1 deletion api/app/chatgpt-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ 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
accessToken: process.env.CHATGPT_TOKEN,
// debug: true
};

const browserClient = async ({ text, progressCallback, convo }) => {
Expand Down
29 changes: 29 additions & 0 deletions api/app/citeText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const citationRegex = /\[\^\d+?\^]/g;

const citeText = (res, noLinks = false) => {
let result = res.text || res;
const citations = Array.from(new Set(result.match(citationRegex)));
if (citations?.length === 0) return result;

if (noLinks) {
citations.forEach((citation) => {
const digit = citation.match(/\d+?/g)[0];
result = result.replaceAll(citation, `<sup>[${digit}](#) </sup>`);
});

return result;
}

let sources = res.details.sourceAttributions;
if (sources?.length === 0) return result;
sources = sources.map((source) => source.seeMoreUrl);

citations.forEach((citation) => {
const digit = citation.match(/\d+?/g)[0];
result = result.replaceAll(citation, `<sup>[${digit}](${sources[digit - 1]}) </sup>`);
});

return result;
};

module.exports = citeText;
13 changes: 13 additions & 0 deletions api/app/getCitations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// const regex = / \[\d+\..*?\]\(.*?\)/g;
const regex = / \[.*?]\(.*?\)/g;

const getCitations = (res) => {
const textBlocks = res.details.adaptiveCards[0].body;
if (!textBlocks) return '';
let links = textBlocks[textBlocks.length - 1]?.text.match(regex);
if (links?.length === 0 || !links) return '';
links = links.map((link) => link.trim());
return links.join('\n');
};

module.exports = getCitations;
6 changes: 6 additions & 0 deletions api/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ const { askClient } = require('./chatgpt-client');
const { browserClient } = require('./chatgpt-browser');
const customClient = require('./chatgpt-custom');
const { askBing } = require('./bingai');
const { askSydney } = require('./sydney');
const titleConvo = require('./titleConvo');
const getCitations = require('./getCitations');
const citeText = require('./citeText');
const detectCode = require('./detectCode');

module.exports = {
askClient,
browserClient,
customClient,
askBing,
askSydney,
titleConvo,
getCitations,
citeText,
detectCode
};
36 changes: 36 additions & 0 deletions api/app/sydney.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');

const askSydney = async ({ text, progressCallback, convo }) => {
const { BingAIClient } = (await import('@waylaidwanderer/chatgpt-api'));

const sydneyClient = 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,
cache: { store: new KeyvFile({ filename: './data/cache.json' }) }
});

let options = {
jailbreakConversationId: true,
onProgress: async (partialRes) => await progressCallback(partialRes),
};

if (convo.parentMessageId) {
options = { ...options, jailbreakConversationId: convo.jailbreakConversationId, parentMessageId: convo.parentMessageId };
}

console.log('sydney options', options);

const res = await sydneyClient.sendMessage(text, options
);

return res;

// for reference:
// https://github.com/waylaidwanderer/node-chatgpt-api/blob/main/demos/use-bing-client.js
};

module.exports = { askSydney };
20 changes: 17 additions & 3 deletions api/models/Conversation.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const convoSchema = mongoose.Schema({
type: String,
default: 'New conversation'
},
jailbreakConversationId: {
type: String
},
conversationSignature: {
type: String
},
Expand Down Expand Up @@ -44,6 +47,15 @@ const convoSchema = mongoose.Schema({
const Conversation =
mongoose.models.Conversation || mongoose.model('Conversation', convoSchema);

const getConvo = async (conversationId) => {
try {
return await Conversation.findOne({ conversationId }).exec();
} catch (error) {
console.log(error);
return { message: 'Error getting single conversation' };
}
};

module.exports = {
saveConvo: async ({ conversationId, title, ...convo }) => {
try {
Expand Down Expand Up @@ -92,12 +104,14 @@ module.exports = {
return { message: 'Error getting conversations' };
}
},
getConvo: async (conversationId) => {
getConvo,
getConvoTitle: async (conversationId) => {
try {
return await Conversation.findOne({ conversationId }).exec();
const convo = await getConvo(conversationId);
return convo.title;
} catch (error) {
console.log(error);
return { message: 'Error getting single conversation' };
return { message: 'Error getting conversation title' };
}
},
deleteConvos: async (filter) => {
Expand Down
3 changes: 2 additions & 1 deletion api/models/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
const { saveMessage, deleteMessages } = require('./Message');
const { getCustomGpts, updateCustomGpt, updateByLabel, deleteCustomGpts } = require('./CustomGpt');
const { getConvo, saveConvo } = require('./Conversation');
const { getConvoTitle, getConvo, saveConvo } = require('./Conversation');

module.exports = {
saveMessage,
deleteMessages,
getConvoTitle,
getConvo,
saveConvo,
getCustomGpts,
Expand Down
14 changes: 7 additions & 7 deletions api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"dependencies": {
"@keyv/mongo": "^2.1.8",
"@vscode/vscode-languagedetection": "^1.0.22",
"@waylaidwanderer/chatgpt-api": "^1.15.1",
"@waylaidwanderer/chatgpt-api": "^1.28.2",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
Expand Down
2 changes: 2 additions & 0 deletions api/server/routes/ask.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const askBing = require('./askBing');
const askSydney = require('./askSydney');
const {
titleConvo,
askClient,
Expand All @@ -13,6 +14,7 @@ const { getConvo, saveMessage, deleteMessages, saveConvo } = require('../../mode
const { handleError, sendMessage } = require('./handlers');

router.use('/bing', askBing);
router.use('/sydney', askSydney);

router.post('/', async (req, res) => {
let { model, text, parentMessageId, conversationId, chatGptLabel, promptPrefix } = req.body;
Expand Down
22 changes: 18 additions & 4 deletions api/server/routes/askBing.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const { titleConvo, askBing } = require('../../app/');
const { titleConvo, getCitations, citeText, askBing } = require('../../app/');
const { saveMessage, deleteMessages, saveConvo } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;

router.post('/', async (req, res) => {
const { model, text, ...convo } = req.body;
Expand All @@ -29,6 +30,7 @@ router.post('/', async (req, res) => {
const progressCallback = async (partial) => {
tokens += partial === text ? '' : partial;
// tokens = appendCode(tokens);
tokens = citeText(tokens, true);
sendMessage(res, { text: tokens, message: true });
};

Expand All @@ -38,8 +40,9 @@ router.post('/', async (req, res) => {
convo
});

console.log('CLIENT RESPONSE');
console.dir(response, { depth: null });
console.log('BING RESPONSE');
// console.dir(response, { depth: null });
const hasCitations = response.response.match(citationRegex)?.length > 0;

userMessage.conversationSignature =
convo.conversationSignature || response.conversationSignature;
Expand All @@ -48,16 +51,27 @@ router.post('/', async (req, res) => {
await saveMessage(userMessage);

if (!convo.conversationSignature) {
response.title = await titleConvo(text, response.response, model);
response.title = await titleConvo({
model,
message: text,
response: JSON.stringify(response.response)
});
}

response.text = response.response;
delete 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;

const links = getCitations(response);
response.text =
citeText(response) +
(links?.length > 0 && hasCitations ? `\n<small>${links}</small>` : '');

await saveMessage(response);
await saveConvo(response);
sendMessage(res, response);
Expand Down
Loading

0 comments on commit 12cf340

Please sign in to comment.