xinghuo-test
Version:
xinghuo
264 lines (238 loc) • 7.78 kB
text/typescript
import CryptoJS from "crypto-js";
import {
ChatDomain,
IChatMessage,
IChatResponse,
ISparkChatResponse,
ModelVersion,
ModelVersionUrl,
} from "./type";
import { ChatRequestParametes } from "./chatRequestParametes";
import createWebSocket from "./createWebSocket";
export class SparkClient {
private appId: string;
private apiKey: string;
private apiSecret: string;
constructor(appId: string, apiKey: string, apiSecret: string) {
this.appId = appId;
this.apiKey = apiKey;
this.apiSecret = apiSecret;
}
getAuthorizationUrl(model = ModelVersion.V2) {
const url = new URL(ModelVersionUrl[model]);
const host = url.host;
const date = new Date().toUTCString();
const algorithm = "hmac-sha256";
const headers = "host date request-line";
const signatureOrigin = `host: ${host}\ndate: ${date}\nGET ${url.pathname} HTTP/1.1`;
const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, this.apiSecret);
const signature = CryptoJS.enc.Base64.stringify(signatureSha);
const authorizationOrigin = `api_key="${this.apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
const authorization = btoa(authorizationOrigin);
return `${url}?authorization=${authorization}&date=${date}&host=${host}`;
}
// chatAsStream(
// model: ModelVersion = ModelVersion.V1_5,
// messages: IChatMessage[],
// parameters?: ChatRequestParametes,
// uid?: string
// ) {
// let params = {
// header: {
// app_id: this.appId,
// uid: uid,
// },
// parameter: {
// chat: {
// domain: ChatDomain[model],
// ...parameters,
// },
// },
// payload: {
// message: {
// text: messages,
// },
// },
// };
// const url = this.getAuthorizationUrl(model);
// let ws = createWebSocket(url);
// const stream = new ReadableStream<IChatResponse>({
// start(controller) {
// ws.onmessage = (event) => {
// function close() {
// ws.close();
// controller.close();
// }
// const resp = JSON.parse(event.data) as ISparkChatResponse;
// if (resp.header.code !== 0) {
// throw new Error(
// `code: ${resp.header.code}, sid: ${resp.header.sid}, message: ${resp.header.message}`
// );
// }
// const text = resp.payload.choices.text
// .map((t) => t.content)
// .join(" ");
// const result = {
// text,
// uasge: resp.payload.usage?.text || null,
// };
// controller.enqueue(result);
// if (resp.header.status === 2) {
// close();
// }
// };
// ws.onopen = () => {
// ws.send(JSON.stringify(params));
// };
// ws.onerror = (event) => {
// throw new Error(JSON.stringify(event));
// };
// },
// });
// return stream;
// }
// async chatAsync(
// model: ModelVersion = ModelVersion.V1_5,
// messages: IChatMessage[],
// callback: (result: IChatResponse) => void,
// parameters?: ChatRequestParametes,
// uid?: string
// ) {
// const stream = this.chatAsStream(model, messages, parameters, uid);
// const reader = await stream.getReader();
// while (true) {
// const { done, value } = await reader.read();
// if (done) {
// break;
// }
// callback(value);
// }
// }
/**
* 异步聊天方法,使用回调来处理聊天响应。
* Asynchronous chat method that uses a callback to handle chat responses.
*
* @param model 模型的版本,例如“V1_5”或“V2”。
* @param model The version of the model, e.g., "V1_5" or "V2".
*
* @param messages 一组聊天信息,由用户和助手的消息组成。
* @param messages A set of chat messages, consisting of messages from the user and assistant.
*
* @param callback 用来处理每一个接收到的聊天响应的回调函数。
* @param callback The callback to process each received chat response.
*
* @param parameters 请求的附加参数(可选)。
* @param parameters Optional additional request parameters.
*
* @param uid 用户的唯一标识符(可选)。
* @param uid User's unique identifier (optional).
*
* @param abortController 可选的AbortController实例,用来取消这个请求。
* @param abortController Optional AbortController instance used to abort this request.
*
*/
async chatAsync(
model: ModelVersion = ModelVersion.V2,
messages: IChatMessage[],
callback: (result: IChatResponse) => void,
parameters?: ChatRequestParametes,
uid?: string,
abortController?: AbortController
) {
const generator = await this.chatAsStreamAsync(
model,
messages,
parameters,
uid,
abortController
);
for await (const message of generator) {
callback(message);
}
}
/**
* 异步实时聊天流方法。
* Asynchronous real-time chat stream method.
*
* @param model 模型的版本,例如“V1_5”或“V2”。
* @param model The version of the model, e.g., "V1_5" or "V2".
*
* @param messages 一组聊天信息,由用户和助手的消息组成。
* @param messages A set of chat messages, consisting of messages from the user and assistant.
*
* @param parameters 请求的附加参数(可选)。
* @param parameters Optional additional request parameters.
*
* @param uid 用户的唯一标识符(可选)。
* @param uid User's unique identifier (optional).
*
* @param abortController 可选的AbortController实例,用来取消这个请求。
* @param abortController Optional AbortController instance used to abort this request.
*
* @return 返回一个Generator,它生成的Promise解析为聊天响应。
* @return Returns a Generator that yields a Promise that resolves to a chat response.
*
*/
*chatAsStreamAsync(
model: ModelVersion = ModelVersion.V1_5,
messages: IChatMessage[],
parameters?: ChatRequestParametes,
uid?: string,
abortController?: AbortController
): Generator<Promise<IChatResponse>> {
let params = {
header: {
app_id: this.appId,
uid: uid,
},
parameter: {
chat: {
domain: ChatDomain[model],
...parameters,
},
},
payload: {
message: {
text: messages,
},
},
};
const url = this.getAuthorizationUrl(model);
let ws = createWebSocket(url);
ws.onopen = () => {
ws.send(JSON.stringify(params));
};
let end = false;
do {
const waitForMessage: Promise<IChatResponse> = new Promise((resolve) => {
ws.onmessage = (event) => {
function close() {
ws.close();
}
const resp = JSON.parse(event.data) as ISparkChatResponse;
if (resp.header.code !== 0) {
throw new Error(
`code: ${resp.header.code}, sid: ${resp.header.sid}, message: ${resp.header.message}`
);
}
const text = resp.payload.choices.text
.map((t) => t.content)
.join(" ");
const result = {
text,
uasge: resp.payload.usage?.text || null,
};
resolve(result);
if (resp.header.status === 2) {
close();
end = true;
}
};
});
yield waitForMessage;
} while (!abortController?.signal.aborted && !end);
if (ws.readyState === 1) {
ws.close();
}
}
}