Skip to content

Commit

Permalink
[PromptCritic] Streaming (#166)
Browse files Browse the repository at this point in the history
Co-authored-by: swyxio <swyxio@users.noreply.github.com>
  • Loading branch information
swyxio and swyxio committed Aug 21, 2023
1 parent 7d76838 commit f2d5dd9
Show file tree
Hide file tree
Showing 16 changed files with 331 additions and 292 deletions.
23 changes: 21 additions & 2 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"@types/jest": "^29.5.2",
"@types/node": "20.2.5",
"@types/react": "^18.2.8",
"@types/react-beautiful-dnd": "^13.1.4",
"@types/react-dom": "^18.2.4",
"@types/react-test-renderer": "^18.0.0",
"@types/terser-webpack-plugin": "^5.0.4",
Expand Down
9 changes: 7 additions & 2 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface ProviderInterface {
fullName: string;
shortName: string;
webviewId: string;
getWebview(): HTMLElement;
getWebview(): HTMLElement | null;
url: string;
paneId(): string;
setupCustomPasteBehavior(): void;
Expand All @@ -16,7 +16,12 @@ export interface ProviderInterface {
getUserAgent(): string;
isEnabled(): boolean;
setEnabled(enabled: boolean): void;
clearCookies(): void;
clearCookies?(): void;

codeForInputElement?: string;
codeForSetInputElementValue?(prompt: string): void;
codeForClickingSubmit?: string;
codeForExtractingResponse?: string;
}

export interface Settings {
Expand Down
91 changes: 91 additions & 0 deletions src/main/apify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { ProviderInterface } from 'lib/types';
import { BrowserWindow } from 'electron';

export async function streamChatResponse(opts: {
provider: ProviderInterface;
prompt: string;
sendFn: (...args: any[]) => void | undefined;
}) {
const win = new BrowserWindow({
// show: true,
show: false,
// titleBarStyle: 'hidden',
// width: 800,
// height: 600,
// webPreferences: {
// webviewTag: true,
// nodeIntegration: true,
// },
});
win.loadURL(opts.provider.url);

return new Promise((resolve, reject) => {
win.webContents.on('dom-ready', async () => {
try {
// check if logged in (and inputElement exists)
await win.webContents.executeJavaScript(
`{${opts.provider.codeForInputElement}}`,
);
} catch (err) {
console.error(
'input element doesnt exist: ',
opts.provider.codeForInputElement,
);
return reject(err);
}
await timeout(500);
const script = `{
${opts.provider.codeForInputElement}
${opts.provider.codeForSetInputElementValue!(opts.prompt)}
${opts.provider.codeForClickingSubmit}
}`;
await win.webContents.executeJavaScript(script);
console.log('script', script);

// Define two variables to store the previous responses
let lastResponseHTML = null;
let secondLastResponseHTML = null;

console.log('looping');
// Loop until our condition is met
await timeout(300);
while (true) {
await timeout(300);
// await win.webContents.executeJavaScript(
// `console.log('hiii', [...document.querySelectorAll('.default.font-sans.text-base.text-textMain .prose')]);`,
// );
var responseHTML = await win.webContents.executeJavaScript(
`${opts.provider.codeForExtractingResponse}.innerHTML`,
);
var responseText = await win.webContents.executeJavaScript(
`${opts.provider.codeForExtractingResponse}.innerText`,
);

console.log({ responseHTML, secondLastResponseHTML });
// If responseHTML hasn't changed for 2 invocations, break
if (
responseHTML === lastResponseHTML &&
responseHTML === secondLastResponseHTML
) {
console.log('prompting');
break;
}

// Shift our stored responses for the next loop iteration
secondLastResponseHTML = lastResponseHTML;
lastResponseHTML = responseHTML;

console.log('sendFn', responseText);
opts.sendFn(responseHTML, responseText); // stream incomplete responses back
}
console.log('closing');
win.close();
return resolve({ responseHTML, responseText });
});
});
}
// thanks claude

function timeout(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
74 changes: 15 additions & 59 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import Store from 'electron-store';
import MenuBuilder from './menu';
import { streamChatResponse } from './apify';
import { resolveHtmlPath } from './util';
import { isValidShortcut } from '../lib/utils';
import PerplexityLlama from '../providers/perplexity-llama';

let store = new Store();

Expand Down Expand Up @@ -66,61 +68,15 @@ ipcMain.on('get-always-on-top', async (event, property, val) => {
event.returnValue = bool;
});

// thanks claude

function timeout(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function getLlamaResponse(prompt: string) {
const win = new BrowserWindow({
// show: true,
show: false,
// titleBarStyle: 'hidden',
// width: 800,
// height: 600,
// webPreferences: {
// webviewTag: true,
// nodeIntegration: true,
// },
ipcMain.on('prompt-hidden-chat', async (event, channel: string, prompt) => {
const sendFn = (...args: any[]) =>
mainWindow?.webContents.send(channel, ...args);
const done = await streamChatResponse({
provider: PerplexityLlama,
prompt,
sendFn,
});
win.loadURL('https://labs.perplexity.ai');
return new Promise((resolve, reject) => {
win.webContents.on('dom-ready', async () => {
await win.webContents.executeJavaScript(`{
var selectElement = document.querySelector('#lamma-select');
selectElement.value = 'llama-2-70b-chat';
var inputElement = document.querySelector('textarea[placeholder*="Ask"]'); // can be "Ask anything" or "Ask follow-up"
inputElement.focus();
var nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
nativeTextAreaValueSetter.call(inputElement, \`${prompt}\`);
var event = new Event('input', { bubbles: true});
inputElement.dispatchEvent(event);
var buttons = Array.from(document.querySelectorAll('button.bg-super'));
var buttonsWithSvgPath = buttons.filter(button => button.querySelector('svg path'));
var button = buttonsWithSvgPath[buttonsWithSvgPath.length - 1];
button.click();
}`);
await timeout(5000);
// const temp = await win.webContents.executeJavaScript(`
// [...document.querySelectorAll('.default.font-sans.text-base.text-textMain .prose')].map(x => x.innerHTML)
// `);
// console.log('temp', temp);
const responseHTML = await win.webContents.executeJavaScript(`
[...document.querySelectorAll('.default.font-sans.text-base.text-textMain .prose')].slice(-1)[0].innerHTML
`);
const responseText = await win.webContents.executeJavaScript(`
[...document.querySelectorAll('.default.font-sans.text-base.text-textMain .prose')].slice(-1)[0].innerText
`);
resolve({ responseHTML, responseText });
win.close();
});
});
}
ipcMain.on('prompt-llama2', async (event, val) => {
const response = await getLlamaResponse(val);
event.returnValue = response;
event.returnValue = done; // {responseHTML, responseText}
});

/*
Expand Down Expand Up @@ -221,11 +177,11 @@ const createWindow = async () => {
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();

// // Open urls in the user's browser
// mainWindow.webContents.setWindowOpenHandler((edata) => {
// shell.openExternal(edata.url);
// return { action: 'allow' };
// });
// Open urls in the user's browser
mainWindow.webContents.setWindowOpenHandler((edata) => {
shell.openExternal(edata.url);
return { action: 'allow' };
});

// Remove this if your app does not use auto updates
// eslint-disable-next-line
Expand Down
7 changes: 3 additions & 4 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint no-unused-vars: off */
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';

export type Channels = 'ipc-example';
export type Channels = 'ipc-example' | 'perplexity-llama2';

const electronHandler = {
ipcRenderer: {
Expand Down Expand Up @@ -44,9 +44,8 @@ const electronHandler = {
setAlwaysOnTop(val: any) {
ipcRenderer.send('set-always-on-top', val);
},
promptLlama2(prompt: string) {
const response = ipcRenderer.sendSync('prompt-llama2', prompt);
return response;
promptHiddenChat(prompt: string) {
ipcRenderer.send('prompt-hidden-chat', 'perplexity-llama2', prompt);
},
},
};
Expand Down
12 changes: 5 additions & 7 deletions src/providers/bing.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,21 @@ class Bing extends Provider {
// SERP Shadow DOM
var serpDOM = document.querySelector('.cib-serp-main');
if (!serpDOM) {
console.error('serpDOM for ${fullName} doesnt exist, have you logged in or are you on the right page?')
}
// Action Bar Shadow DOM
var inputDOM = serpDOM.shadowRoot.querySelector('#cib-action-bar-main');
if (!inputDOM) {
console.error('inputDOM for ${fullName} doesnt exist, have you logged in or are you on the right page?')
}
// Text Input Shadow DOM
var textInputDOM = inputDOM.shadowRoot.querySelector('cib-text-input');
// This inner cib-text-input Shadow DOM is not always present
var inputElement = textInputDOM ? textInputDOM.shadowRoot.querySelector('#searchbox') : inputDOM.shadowRoot.querySelector('#searchbox');
simulateUserInput(inputElement, \`${input}\`);
if (!inputElement) {
console.error('inputElement for \`${fullName}\` doesnt exist, have you logged in or are you on the right page?')
} else {
simulateUserInput(inputElement, \`${input}\`);
}
}
`);
}
Expand Down
2 changes: 1 addition & 1 deletion src/providers/claude2.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class Claude2 extends Provider {
}

static isEnabled() {
return window.electron.electronStore.get(`${this.webviewId}Enabled`, false);
return window.electron.electronStore.get(`${this.webviewId}Enabled`, true);
}
}

Expand Down
19 changes: 19 additions & 0 deletions src/providers/perplexity-llama.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,25 @@ class PerplexityLlama extends Provider {
}
}

static codeForInputElement = `var inputElement = document.querySelector('textarea[placeholder*="Ask"]');`;
static codeForSetInputElementValue(prompt) {
return `
var nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
nativeTextAreaValueSetter.call(inputElement, \`${prompt}\`);
var event = new Event('input', { bubbles: true});
inputElement.dispatchEvent(event);
`;
}
static codeForClickingSubmit = `
var buttons = Array.from(document.querySelectorAll('button.bg-super'));
var buttonsWithSvgPath = buttons.filter(button => button.querySelector('svg path'));
var button = buttonsWithSvgPath[buttonsWithSvgPath.length - 1];
button.click();
`;
static codeForExtractingResponse = `[...document.querySelectorAll('.default.font-sans.text-base.text-textMain .prose')].slice(-1)[0]`; // dont append semicolon, we will append innerhtml etc

static handleSubmit() {
try {
this.getWebview().executeJavaScript(`{
Expand Down
2 changes: 1 addition & 1 deletion src/providers/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class Provider {
}

static getUserAgent() {
return false;
return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.37';
}

static isEnabled() {
Expand Down
10 changes: 6 additions & 4 deletions src/providers/smol.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ class SmolTalk extends Provider {
this.getWebview().executeJavaScript(`{
var btn = document.querySelector('#smol-submitbtn');
if (btn) {
btn.focus();
btn.setAttribute("aria-disabled", "false"); // doesnt work alone
btn.disabled = false;
btn.click()
}
btn.focus();
btn.setAttribute("aria-disabled", "false"); // doesnt work alone
btn.disabled = false;
btn.click()
}`);
}

Expand Down

0 comments on commit f2d5dd9

Please sign in to comment.