diff --git a/package.json b/package.json
index 5a1877a..352118f 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,8 @@
"dependencies": {
"@kangc/v-md-editor": "^2.3.15",
"element-plus": "^2.3.4",
+ "nanoid": "^4.0.2",
+ "node-forge": "^1.3.1",
"query-string": "^8.1.0",
"sass": "^1.62.1",
"sass-loader": "^13.2.2",
diff --git a/src/components/chat.vue b/src/components/chat.vue
index 902f49f..9676936 100644
--- a/src/components/chat.vue
+++ b/src/components/chat.vue
@@ -65,7 +65,7 @@ export default {
}
this.chatHistory.push(aiMsg)
if (this.config.stream) {
- ai.chatStream(this.chatHistory).then(reader => this.streamOutput(reader, this.chatHistory.at(-1))).finally(() => this.loading = false)
+ ai.chatStream(this.chatHistory).then(reader => ai.streamOutput(reader, this.chatHistory.at(-1))).finally(() => this.loading = false)
} else {
ai.chat(this.chatHistory).then(reply => {
const decodeArr = reply.split("")
@@ -75,40 +75,6 @@ export default {
} else {
this.$message.error("请不要发送空消息!")
}
- },
- streamOutput(reader, chat) {
- return reader.read().then(({done, value}) => {
- if (done) {
- return;
- }
- if (!chat.reminder) {
- chat.reminder = ""
- }
- let decode = new TextDecoder().decode(value)
- decode = chat.reminder + decode
- let decodedArray = decode.split("data: ");
- let longstr = "";
- decodedArray.forEach(decoded => {
- decoded = decoded.trim();
- try {
- if (longstr != "") {
- decoded = longstr + decoded;
- longstr = "";
- }
- } catch (e) {
- longstr = decoded;
- decoded = "";
- }
- if (!!decoded && decoded !== "[DONE]") {
- const choices = JSON.parse(decoded).choices
- if (choices?.length > 0) {
- const response = choices[0].delta.content || "";
- chat.msg += response
- }
- }
- })
- return this.streamOutput(reader, chat)
- })
}
}
}
diff --git a/src/components/settings.vue b/src/components/settings.vue
index a454898..81cbd85 100644
--- a/src/components/settings.vue
+++ b/src/components/settings.vue
@@ -4,14 +4,17 @@
+ @click="initModel(m)"/>
- settings.model.setApiKey(v),getModelAccount()"/>
+
+
+ 应用
+
{{ account.username }}
@@ -46,6 +49,7 @@ export default {
immediate: true,
handler(v) {
this.settings = v
+ this.getModelAccount()
}
}
},
@@ -54,15 +58,23 @@ export default {
account: v => v.settings.account || {usage: 0, total: 0}
},
methods: {
+ initModel(model) {
+ const ins = new model()
+ const timer = setInterval(() => {
+ if (ins.apiKey) {
+ clearInterval(timer)
+ this.settings.model = ins
+ this.getModelAccount()
+ }
+ }, 500)
+ },
getModelAccount() {
const ai = this.settings.model
- if (ai.getAccount) {
+ if (ai.apiKey) {
this.loadingAccount = true
ai.getAccount().then(v => this.settings.account = v).finally(() => this.loadingAccount = false)
}
}
- },
- created() {
}
}
diff --git a/src/utils/models.js b/src/utils/models.js
index 5a07abf..16165c6 100644
--- a/src/utils/models.js
+++ b/src/utils/models.js
@@ -1,4 +1,6 @@
import {dayjs} from "element-plus";
+import {nanoid} from "nanoid";
+import forge from 'node-forge';
import axios from "./axios";
import {AI_AVATAR, OPEN_AI_KEY} from "./env";
@@ -9,7 +11,7 @@ class BaseModel {
}
this.headers = {
"Content-Type": "application/json",
- Accept: "application/json",
+ Accept: "application/json,text/event-stream",
}
}
@@ -19,6 +21,9 @@ class BaseModel {
}
}
+/**
+ * ChatGPT gpt-3.5-turbo的api.
+ */
export class ChatGPT extends BaseModel {
static base = "https://chatwithai.pages.dev"
static avatar = AI_AVATAR
@@ -69,6 +74,41 @@ export class ChatGPT extends BaseModel {
});
} else return Promise.reject("没有权限或者网络异常,请重新尝试!")
}
+
+ streamOutput(reader, chat) {
+ return reader.read().then(({done, value}) => {
+ if (done) {
+ return;
+ }
+ if (!chat.reminder) {
+ chat.reminder = ""
+ }
+ let decode = new TextDecoder().decode(value)
+ decode = chat.reminder + decode
+ let decodedArray = decode.split("data: ");
+ let longstr = "";
+ decodedArray.forEach(decoded => {
+ decoded = decoded.trim();
+ try {
+ if (longstr != "") {
+ decoded = longstr + decoded;
+ longstr = "";
+ }
+ } catch (e) {
+ longstr = decoded;
+ decoded = "";
+ }
+ if (!!decoded && decoded !== "[DONE]") {
+ const choices = JSON.parse(decoded).choices
+ if (choices?.length > 0) {
+ const response = choices[0].delta.content || "";
+ chat.msg += response
+ }
+ }
+ })
+ return this.streamOutput(reader, chat)
+ })
+ }
}
/**
@@ -76,26 +116,80 @@ export class ChatGPT extends BaseModel {
*/
export class ChatGLM extends BaseModel {
static base = "https://maas.aminer.cn/api/paas"
+ "/model/v1/open/engines/chatGLM/chatGLM"
static avatar = "https://cdn.cunwuyun.cn/chat/chatglm.svg"
static name = "ChatGLM"
static id = "chatglm-130b"
static desc = "ChatGLM-130B所基于的模型"
+ static publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMZXxmDh2Rs1lh3Ymud1eVBjds/9SfjczHJFpNe9+0FsUffILVMTBcTqmdPZxjC6M1Ad2EHaHMWXZuc0fIc4Lh8CAwEAAQ=="
constructor(params) {
const {avatar, name, desc, id} = ChatGLM
- super({avatar, name, desc, id, ...params})
+ super({avatar, name, desc, id, taskId: nanoid(), ...params})
this.getToken().then(e => this.setApiKey(e))
}
- getToken() {
- const encrypted = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMZXxmDh2Rs1lh3Ymud1eVBjds/9SfjczHJFpNe9+0FsUffILVMTBcTqmdPZxjC6M1Ad2EHaHMWXZuc0fIc4Lh8CAwEAAQ=="
- return axios.post(ChatGLM.base + "/passApiToken/createApiToken", JSON.stringify({apiKey: "4e3ceff669c143dfa09e763663aa72cd", encrypted}), {
+ async getToken() {
+ if (this.apiKey) return await this.apiKey
+ const timestamp = new TextEncoder().encode(Date.now().toFixed(0))
+ const encrypted = ChatGLM.encrypt(ChatGLM.publicKey, timestamp)
+ return await axios.post(ChatGLM.base + "/passApiToken/createApiToken", JSON.stringify({apiKey: "4e3ceff669c143dfa09e763663aa72cd", encrypted}), {
headers: this.headers,
- }).then(res => res.json()).then(data => data?.token || "key无效或网络波动,请重新尝试");
+ }).then(res => res.json()).then(data => data?.data || "key无效或网络波动,请重新尝试");
}
async chat(history, callback) {
const context = await axios.post(ChatGLM.base + "/v1/stream_context").then(res => res.json());
return await axios.get(ChatGPT.base + "/v1/stream", {params: context.result})
}
+
+ async chatStream(messages) {
+ const history = messages.map(e => e.msg)
+ history.pop()
+ const prompt = history.pop()
+ const url = ChatGLM.base + "/model/v1/open/engines/sse/chatGLM/chatGLM"
+ // const url = ChatGLM.base + "/model/v2/open/engines/chatglm_qa_6b/chatglm_6b"
+ return await axios.post(url, JSON.stringify({
+ history, prompt,
+ temperature: 1, top_p: 0.6, requestTaskNo: this.taskId
+ }), {
+ headers: this.headers,
+ }).then(res => res?.body?.getReader());
+ }
+
+ static encrypt(publicKey, timestamp) {
+ const public_key = forge.util.decode64(publicKey)
+ const decoded_key = forge.asn1.fromDer(public_key); // 使用 fromDer 方法解码
+ const key = forge.pki.publicKeyFromAsn1(decoded_key); // 使用 publicKeyFromAsn1 方法导入公钥
+ const encrypted = key.encrypt(timestamp, 'RSAES-PKCS1-V1_5');
+ return forge.util.encode64(encrypted);
+ }
+
+ async getAccount() {
+ const {headers} = this
+ const usages = await axios.get("https://open.bigmodel.ai/api/paas/account/query-customer-account-report", {headers}).then(res => res.json());
+ if (usages.code == 200) {
+ const {data} = usages
+ return {
+ ...data, username: "Kubbo",
+ usage: data.totalSpendAmount?.toFixed(4),
+ total: data.rechargeAmount?.toFixed(4)
+ }
+ } else return Promise.reject("没有权限或者网络异常,请重新尝试!")
+ }
+
+ streamOutput(reader, chat) {
+ return reader.read().then(({done, value}) => {
+ if (done) {
+ return;
+ }
+ const decode = new TextDecoder().decode(value)
+ const contents = decode.split("event:finish")[0].split("\n")
+ if (contents.length > 0) {
+ console.log(contents)
+ chat.msg = contents.filter(e => e.startsWith("data:") && e.trim() != "data:")?.map(e => e.replace(/data:/, '')).join("\n") || ""
+ }
+ return this.streamOutput(reader, chat)
+ })
+ }
}