Skip to content

Commit

Permalink
feat: Add phind (ai-shifu#560)
Browse files Browse the repository at this point in the history
* Add CommonBotSettings Select type

* Add get-url-cookies ipc handler

* Add Phind

* Fix incorrectly replaced URLs
For instance, "Source1" and "Source10" produced "abc.com" and "abc.com0" respectively.

* Use model setting, replace link [^1^]

* Add setPhind mutations type

* add error handler

* Show newline properly

---------

Co-authored-by: Sunner Sun <[email protected]>
  • Loading branch information
tanchekwei and sunner authored Sep 30, 2023
1 parent 31937dd commit fed863b
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 2 deletions.
13 changes: 13 additions & 0 deletions public/bots/phind-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,13 @@ function createNewWindow(url, userAgent = "") {
});
}

async function getCookies(filter) {
const cookies = await mainWindow.webContents.session.cookies.get({
...filter,
});
return cookies;
}

ipcMain.handle("create-new-window", (event, url, userAgent) => {
createNewWindow(url, userAgent);
});
Expand Down Expand Up @@ -358,6 +365,10 @@ ipcMain.handle("save-proxy-and-restart", async () => {
});
// Proxy Setting End

ipcMain.handle("get-cookies", async (event, filter) => {
return await getCookies(filter);
});

nativeTheme.on("updated", () => {
mainWindow.webContents.send("on-updated-system-theme");
});
Expand Down
211 changes: 211 additions & 0 deletions src/bots/PhindBot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import Bot from "@/bots/Bot";
import store from "@/store";
import AsyncLock from "async-lock";
import axios from "axios";
import { SSE } from "sse.js";
const { ipcRenderer } = window.require("electron");

export default class PhindBot extends Bot {
static _brandId = "phind"; // Brand id of the bot, should be unique. Used in i18n.
static _className = "PhindBot"; // Class name of the bot
static _logoFilename = "phind-logo.svg"; // Place it in public/bots/
static _isDarkLogo = true; // The main color of logo is dark
static _loginUrl = "https://www.phind.com";
static _lock = new AsyncLock(); // AsyncLock for prompt requests

constructor() {
super();
}

/**
* Check whether the bot is logged in, settings are correct, etc.
* @returns {boolean} - true if the bot is available, false otherwise.
*/
async _checkAvailability() {
return true;
}

/**
* Send a prompt to the bot and call onResponse(response, callbackParam)
* when the response is ready.
* @param {string} prompt
* @param {function} onUpdateResponse params: callbackParam, Object {content, done}
* @param {object} callbackParam - Just pass it to onUpdateResponse() as is
*/
async _sendPrompt(prompt, onUpdateResponse, callbackParam) {
try {
const context = await this.getChatContext();
const rewrite = await axios.post(
"https://www.phind.com/api/infer/followup/rewrite",
{
questionToRewrite: prompt,
questionHistory: context.questionHistory,
answerHistory: context.answerHistory,
},
);
const search = await axios.post("https://www.phind.com/api/web/search", {
q: rewrite.data.query,
browserLanguage: "en-GB",
userSearchRules: {},
});

const date = new Date();
const formatDate = this.getFormattedDate(date);
const payload = JSON.stringify({
questionHistory: context.questionHistory,
answerHistory: context.answerHistory,
question: prompt,
webResults: search.data,
options: {
date: formatDate,
language: "en-GB",
detailed: true,
anonUserId: await this.getUUID(),
answerModel: store.state.phind.model,
customLinks: [],
},
context: "",
});

return new Promise((resolve, reject) => {
try {
const source = new SSE("https://www.phind.com/api/infer/answer", {
start: false,
payload,
});
let text = "";
let isSuccess = false;
source.addEventListener("message", (event) => {
if (event.data) {
if (event.data.startsWith("<PHIND_METADATA>")) {
isSuccess = true;
} else {
text += event.data;
onUpdateResponse(callbackParam, {
content: text,
done: false,
});
}
}
});

source.addEventListener("readystatechange", (event) => {
if (event.readyState === source.CLOSED) {
// after stream closed, done
if (isSuccess) {
// save answerHistory and questionHistory to context
this.setChatContext({
answerHistory: [...context.answerHistory, text],
questionHistory: [...context.questionHistory, prompt],
});

// replace link with hostname
if (search.data && search.data.length) {
for (let i = 0; i < search.data.length; i++) {
const hostname = new URL(search.data[i].url).hostname;
text = text.replaceAll(`[Source${i}]`, `[${hostname}]`);
text = text.replaceAll(
`[^${i}^]`,
` [${hostname}](${search.data[i].url})`,
);
text = text.replaceAll(
`^${i}^`,
` [${hostname}](${search.data[i].url})`,
);
}
}
}
onUpdateResponse(callbackParam, {
content: text,
done: true,
});
resolve();
}
});
source.addEventListener("error", (event) => {
console.error(event);
reject(this.getSSEDisplayError(event));
});

// override default _onStreamProgress to fix missing new line in response due to trimming
source._onStreamProgress = function (e) {
if (!source.xhr) {
return;
}

if (source.xhr.status !== 200) {
source._onStreamFailure(e);
return;
}

if (source.readyState == source.CONNECTING) {
source.dispatchEvent(new CustomEvent("open"));
source._setReadyState(source.OPEN);
}

var data = source.xhr.responseText.substring(source.progress);

source.progress += data.length;
var parts = (source.chunk + data).split(/\r\n\r\n/);
var lastPart = parts.pop();
for (let part of parts) {
// skip if data is empty
if (part === "data: ") {
continue;
}

// newline
if (part === "data: \r\ndata: ") {
let event = new CustomEvent("message");
event.data = "\n";
source.dispatchEvent(event);
continue;
}

const event = source._parseEventChunk(part);
source.dispatchEvent(event);
}
source.chunk = lastPart;
};
source.stream();
} catch (err) {
reject(err);
}
});
} catch (error) {
if (error.request.status === 403) {
throw new Error(
`${error.request.status} ${error.request.responseText}`,
);
} else {
console.error("Error PhindBot _sendPrompt:", error);
throw error;
}
}
}

/**
* Should implement this method if the bot supports conversation.
* The conversation structure is defined by the subclass.
* @param null
* @returns {any} - Conversation structure. null if not supported.
*/
async createChatContext() {
return { answerHistory: [], questionHistory: [] };
}

getFormattedDate(date) {
let year = date.getFullYear();
let month = (1 + date.getMonth()).toString().padStart(2, "0");
let day = date.getDate().toString().padStart(2, "0");
return month + "/" + day + "/" + year;
}

async getUUID() {
const cookies = await ipcRenderer.invoke("get-cookies", {
domain: "www.phind.com",
});
const uuidCookie = cookies.find((cookie) => cookie.name === "uuid");
return uuidCookie ? uuidCookie.value : "";
}
}
3 changes: 3 additions & 0 deletions src/bots/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import OpenAIAPI3516KBot from "./openai/OpenAIAPI3516KBot";
import AzureOpenAIAPIBot from "./microsoft/AzureOpenAIAPIBot";
import WenxinQianfanTurboBot from "./baidu/WenxinQianfanTurboBot";
import YouChatBot from "./YouChatBot";
import PhindBot from "./PhindBot";
import PiBot from "./PiBot";
import Qihoo360AIBrainBot from "./Qihoo360AIBrainBot";
import OpenAssistantBot from "./OpenAssistantBot";
Expand Down Expand Up @@ -88,6 +89,7 @@ const all = [
MOSSBot.getInstance(),
HuggingChatBot.getInstance(),
OpenAssistantBot.getInstance(),
PhindBot.getInstance(),
PiBot.getInstance(),
QianWenBot.getInstance(),
SkyWorkBot.getInstance(),
Expand Down Expand Up @@ -150,6 +152,7 @@ export const botTags = {
bots.getBotByClassName("Vicuna13bBot"),
bots.getBotByClassName("CharacterAIBot"),
bots.getBotByClassName("ClaudeAIBot"),
bots.getBotByClassName("PhindBot"),
bots.getBotByClassName("PiBot"),
bots.getBotByClassName("SageBot"),
bots.getBotByClassName("Falcon180bBot"),
Expand Down
14 changes: 14 additions & 0 deletions src/components/BotSettings/CommonBotSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@
store.commit(mutationType, { [setting.name]: $event })
"
></v-text-field>
<v-select
v-if="setting.type === Type.Select"
v-model="settingState[setting.name]"
outlined
dense
:label="setting.label"
:placeholder="setting.placeholder"
:hide-details="setting.hideDetails"
:items="setting.items"
@update:model-value="
/* setFalcon({ temperature: $event }) */
store.commit(mutationType, { [setting.name]: $event })
"
></v-select>
<v-slider
v-else-if="setting.type === Type.Slider"
v-model="settingState[setting.name] /* falcon.temperature */"
Expand Down
33 changes: 33 additions & 0 deletions src/components/BotSettings/PhindBotSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<CommonBotSettings
:settings="settings"
:brand-id="brandId"
mutation-type="setPhind"
></CommonBotSettings>
</template>

<script>
import Bot from "@/bots/PhindBot";
import CommonBotSettings from "@/components/BotSettings/CommonBotSettings.vue";
import { Type } from "./settings.const";
const settings = [
{
type: Type.Select,
name: "model",
title: "Model",
items: ["Phind Model", "GPT-3.5-Turbo", "GPT-4"],
},
];
export default {
components: {
CommonBotSettings,
},
data() {
return {
settings: settings,
brandId: Bot._brandId,
};
},
};
</script>
1 change: 1 addition & 0 deletions src/components/BotSettings/settings.const.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const Type = {
Text: 0,
Slider: 1,
Select: 2,
};
2 changes: 2 additions & 0 deletions src/components/SettingsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ import QianWenBotSettings from "@/components/BotSettings/QianWenBotSettings.vue"
import PoeBotSettings from "@/components/BotSettings/PoeBotSettings.vue";
import SkyWorkBotSettings from "@/components/BotSettings/SkyWorkBotSettings.vue";
import YouChatBotSettings from "@/components/BotSettings/YouChatBotSettings.vue";
import PhindBotSettings from "@/components/BotSettings/PhindBotSettings.vue";
import PiBotSettings from "@/components/BotSettings/PiBotSettings.vue";
import Qihoo360AIBrainBotSettings from "./BotSettings/Qihoo360AIBrainBotSettings.vue";
import OpenAssistantBotSettings from "./BotSettings/OpenAssistantBotSettings.vue";
Expand Down Expand Up @@ -143,6 +144,7 @@ const botSettings = [
{ brand: "moss", component: MOSSBotSettings },
{ brand: "openaiApi", component: OpenAIAPIBotSettings },
{ brand: "openAssistant", component: OpenAssistantBotSettings },
{ brand: "phind", component: PhindBotSettings },
{ brand: "pi", component: PiBotSettings },
{ brand: "poe", component: PoeBotSettings },
{ brand: "qianWen", component: QianWenBotSettings },
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@
"repetitionPenalty": "Repetition penalty",
"repetitionPenaltyPrompt": "Penalize repeated tokens"
},
"phind": {
"name": "Phind"
},
"dev": {
"name": "Dev Bot"
},
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@
"name": "Falcon",
"falcon-180b": "180b"
},
"phind": {
"name": "Phind"
},
"dev": {
"name": "开发专用 Bot"
},
Expand Down
7 changes: 5 additions & 2 deletions src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ export default createStore({
topP: 0.9,
repetitionPenalty: 1.2,
},
phind: {
model: "Phind Model",
},
chats: [
{
title: "New Chat",
Expand Down Expand Up @@ -210,8 +213,8 @@ export default createStore({
setPoe(state, values) {
state.poe = { ...state.poe, ...values };
},
setFalcon(state, values) {
state.falcon = { ...state.falcon, ...values };
setPhind(state, values) {
state.phind = { ...state.phind, ...values };
},
setLatestPromptIndex(state, promptIndex) {
const currentChat = state.chats[state.currentChatIndex];
Expand Down

0 comments on commit fed863b

Please sign in to comment.