Skip to content

Commit

Permalink
feat: add Kimi chat bot
Browse files Browse the repository at this point in the history
  • Loading branch information
sunner committed Mar 17, 2024
1 parent d10f71c commit fc19733
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 0 deletions.
Binary file added public/bots/kimi-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,13 @@ function createNewWindow(url, userAgent = "") {
} else if (url.startsWith("https://chatglm.cn/")) {
const token = await getCookie("chatglm_token");
mainWindow.webContents.send("CHATGLM-TOKENS", { token });
} else if (url.startsWith("https://kimi.moonshot.cn/")) {
const access_token = await getLocalStorage("access_token");
const refresh_token = await getLocalStorage("refresh_token");
mainWindow.webContents.send("KIMI-TOKENS", {
access_token,
refresh_token,
});
}
} catch (err) {
console.error(err);
Expand Down
4 changes: 4 additions & 0 deletions src/bots/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import Gemma2bItBot from "./lmsys/Gemma2bItBot";
import Claude3SonnetBot from "./lmsys/Claude3SonnetBot";
import Claude3OpusBot from "./lmsys/Claude3OpusBot";
import ChatGLM4Bot from "./zhipu/ChatGLM4Bot";
import KimiBot from "./moonshot/KimiBot";

const all = [
Qihoo360AIBrainBot.getInstance(),
Expand Down Expand Up @@ -111,6 +112,7 @@ const all = [
OpenAIAPI4128KBot.getInstance(),
ChatGPT432kPoeBot.getInstance(),
GradioAppBot.getInstance(),
KimiBot.getInstance(),
Llama27bBot.getInstance(),
Llama213bBot.getInstance(),
Llama270bPoeBot.getInstance(),
Expand Down Expand Up @@ -211,6 +213,7 @@ export const botTags = {
bots.getBotByClassName("Gemma7bItBot"),
bots.getBotByClassName("Claude3SonnetBot"),
bots.getBotByClassName("Claude3OpusBot"),
bots.getBotByClassName("KimiBot"),
],
paid: [
bots.getBotByClassName("ChatGPT4Bot"),
Expand Down Expand Up @@ -274,6 +277,7 @@ export const botTags = {
bots.getBotByClassName("ChatGLM4Bot"),
bots.getBotByClassName("ChatGLM6bBot"),
bots.getBotByClassName("ChatGLM36bBot"),
bots.getBotByClassName("KimiBot"),
],
};
export default bots;
166 changes: 166 additions & 0 deletions src/bots/moonshot/KimiBot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import AsyncLock from "async-lock";
import Bot from "@/bots/Bot";
import axios from "axios";
import store from "@/store";
import { SSE } from "sse.js";
import i18n from "@/i18n";

export default class KimiBot extends Bot {
static _brandId = "kimi"; // Brand id of the bot, should be unique. Used in i18n.
static _className = "KimiBot"; // Class name of the bot
static _logoFilename = "kimi-logo.png"; // Place it in public/bots/
static _loginUrl = "https://kimi.moonshot.cn/";
static _lock = new AsyncLock(); // AsyncLock for prompt requests

constructor() {
super();
}

getAuthHeader() {
return {
headers: {
Authorization: `Bearer ${store.state.kimi?.access_token}`,
},
};
}

/**
* Check whether the bot is logged in, settings are correct, etc.
* @returns {boolean} - true if the bot is available, false otherwise.
*/
async _checkAvailability() {
let available = false;
let userInfoUrl = "https://kimi.moonshot.cn/api/user";
await axios
.get(userInfoUrl, this.getAuthHeader())
.then((response) => {
available = response.data?.status == "normal";
})
.catch(async (error) => {
if (
error.response?.status == 401 &&
error.response?.data?.error_type == "auth.token.invalid"
) {
await this.refreshTokens();
available = await this._checkAvailability();
} else {
console.error("Error checking Kimi login status:", error);
}
});

return available;
}

async refreshTokens() {
let refreshUrl = "https://kimi.moonshot.cn/api/auth/token/refresh";
await axios
.get(refreshUrl, {
headers: {
Authorization: `Bearer ${store.state.kimi?.refresh_token}`,
},
})
.then((response) => {
store.commit("setKimi", {
access_token: response.data?.access_token,
refresh_token: response.data?.refresh_token,
});
})
.catch((error) => {
console.error("Error refreshing Kimi tokens:", error);
});
}

/**
* 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) {
let context = await this.getChatContext();

return new Promise((resolve, reject) => {
const headers = this.getAuthHeader().headers;
headers["Content-Type"] = "application/json";
try {
const source = new SSE(
`https://kimi.moonshot.cn/api/chat/${context.chat}/completion/stream`,
{
headers,
payload: JSON.stringify({
messages: [
{
role: "user",
content: prompt,
},
],
refs: [],
use_search: true,
}),
withCredentials: true,
},
);

let beginning = "";
let body = "";
source.addEventListener("message", (event) => {
const data = JSON.parse(event.data);

if (data.event === "search_plus") {
if (data.msg?.type == "start_res")
beginning += `> ${i18n.global.t("kimi.searching")}\n`;
else if (data.msg?.type === "get_res")
beginning += `> ${i18n.global.t("kimi.found", { num: data.msg.successNum })}[${data.msg.title}](${data.msg.url})\n`;
} else if (data.event === "cmpl") {
body += data.text;
}

if (data.event === "all_done") {
onUpdateResponse(callbackParam, {
content: `${beginning}\n${body}`,
done: true,
});
resolve();
} else {
onUpdateResponse(callbackParam, {
content: `${beginning}\n${body}`,
done: false,
});
}
});
source.stream();
} catch (err) {
reject(err);
}
});
}

/**
* 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() {
let context = null;
await axios
.post(
"https://kimi.moonshot.cn/api/chat",
{
is_example: false,
name: "ChatALL",
},
this.getAuthHeader(),
)
.then((response) => {
context = {
chat: response.data?.id,
};
})
.catch((error) => {
console.error("Error Kimi createChatContext ", error);
});
return context;
}
}
32 changes: 32 additions & 0 deletions src/components/BotSettings/KimiBotSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<template>
<login-setting :bot="bot"></login-setting>
</template>

<script>
const electron = window.require("electron");
const ipcRenderer = electron.ipcRenderer;
import { mapMutations } from "vuex";
import Bot from "@/bots/moonshot/KimiBot";
import LoginSetting from "@/components/BotSettings/LoginSetting.vue";
export default {
components: {
LoginSetting,
},
data() {
return {
bot: Bot.getInstance(),
};
},
mounted() {
// Listen for the KIMI-TOKENS message from background.js
ipcRenderer.on("KIMI-TOKENS", (event, tokens) => {
this.setKimi(tokens);
});
},
methods: {
...mapMutations(["setKimi"]),
},
};
</script>
5 changes: 5 additions & 0 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@
"urlPrompt": "URL of your Gradio app. If using Hugging Face space, get the direct url by clicking the \"Embed this space\" in the upper right corner.",
"waiting": "There are { queue_size } requests in the queue, you are in { rank }th place, and you are expected to wait for { queue_eta } seconds"
},
"kimi": {
"name": "Kimi",
"found": "Found {num}: ",
"searching": "Searching..."
},
"lmsys": {
"name": "LMSYS",
"vicuna-7b": "vicuna-7b",
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@
"urlPrompt": "您自己的 Gradio app URL。如使用 Hugging Face 的 space,点击 space 右上角的 \"Embed this space\" 获取 direct URL",
"waiting": "当前有 { queue_size } 个请求在队列中,你在第 { rank } 位,预计还要等待 { queue_eta } 秒"
},
"kimi": {
"name": "Kimi",
"found": "找到了第 {num} 篇资料:",
"searching": "搜索中..."
},
"lmsys": {
"name": "LMSYS",
"vicuna-7b": "vicuna-7b",
Expand Down
7 changes: 7 additions & 0 deletions src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ export default createStore({
chatGlm: {
token: "",
},
kimi: {
access_token: "",
refresh_token: "",
},
qianWen: {
xsrfToken: "",
},
Expand Down Expand Up @@ -199,6 +203,9 @@ export default createStore({
setSkyWork(state, tokens) {
state.skyWork = { ...state.skyWork, ...tokens };
},
setKimi(state, tokens) {
state.kimi = { ...state.kimi, ...tokens };
},
setWenxinQianfan(state, values) {
state.wenxinQianfan = { ...state.wenxinQianfan, ...values };
},
Expand Down

0 comments on commit fc19733

Please sign in to comment.