UNPKG

web2api-server

Version:

server part of web2api service(1/2)

116 lines (102 loc) 2.71 kB
/** * store state of client extension in memory */ import type { WSContext } from "hono/ws"; import type { ModelId, TxChatType } from "./type.js"; import { uid } from "radash"; import { createManagedStream } from "@/lib/stream.js"; import type { AddtionalChatCompletionRequestParams, Message, } from "@/handler/completion-types.js"; /** * Manage the interaction with client(browser extension) */ class ClientManager { private ws: WSContext | undefined; private writersMap: Map< string, ReturnType<typeof createManagedStream>["writer"] > = new Map(); #provideModels: Record<string, string[]>; #clientVersion: string = ""; constructor() { this.#provideModels = {}; } setWsContext(ws: WSContext) { if (this.ws && this.ws.readyState === 1) { // this.ws.close(); } this.ws = ws; } get state() { return this.ws?.readyState ?? -1; // -1 means not connect } setProvideModels(provideModels: Record<string, string[]>) { this.#provideModels = provideModels; } get provideModels() { return this.#provideModels; } includeModel(modelId: string) { return !!Object.values(this.#provideModels).find((arr) => arr.includes(modelId) ); } setClientVersion(version: string) { this.#clientVersion = version; } get clientVersion() { return this.#clientVersion; } /* * mutations */ /** * send startChat to client-side */ startChat( model: ModelId, msg: Message[], addtionalParameter?: AddtionalChatCompletionRequestParams ) { if (!this.ws || this.state !== 1) { throw new Error("ws is not ready, state:" + this.state); } const id = "chatcmpl-" + uid(16); const message: TxChatType = { type: "startChat", model: model, id: id, content: msg, options: addtionalParameter, }; this.ws.send(JSON.stringify(message)); const { stream, writer } = createManagedStream(); this.writersMap.set(id, writer); //auto delete after 5 mins setTimeout(() => { writer.tryClose(); this.writersMap.delete(id); }, 300_000); return { id, stream, writer }; } /** * transform textPart from client-side to the readStream created in `startChat` */ enqueueChatStream(id: string, part: string, flag?: "close" | "error") { const writer = this.writersMap.get(id); if (!writer) { throw new Error("writer not exist for id:" + id); } if (flag === "close") { writer.close(); this.writersMap.delete(id); } else if (flag === "error") { writer.error(part); } else { writer.write(part); } } } export const globalClientManager = new ClientManager();