2024-07-08 18:35:34 +08:00
|
|
|
import axios, { AxiosResponse, CancelTokenSource } from "axios";
|
2024-07-08 22:18:00 +08:00
|
|
|
import * as path from "path";
|
|
|
|
import * as fs from "fs";
|
|
|
|
import * as os from "os";
|
2024-07-08 18:35:34 +08:00
|
|
|
|
|
|
|
import { logger } from "../util/logger";
|
|
|
|
import { getFileContent } from "../util/commonUtil";
|
|
|
|
|
|
|
|
import { UiUtilWrapper } from "../util/uiUtil";
|
|
|
|
|
2024-07-16 07:55:02 +08:00
|
|
|
|
|
|
|
class DCLocalServicePortNotSetError extends Error {
|
|
|
|
constructor() {
|
|
|
|
super("DC_LOCALSERVICE_PORT is not set");
|
|
|
|
this.name = "DCLocalServicePortNotSetError";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-08 18:35:34 +08:00
|
|
|
function timeThis(
|
|
|
|
target: Object,
|
|
|
|
propertyKey: string,
|
|
|
|
descriptor: TypedPropertyDescriptor<any>
|
|
|
|
) {
|
|
|
|
const originalMethod = descriptor.value;
|
|
|
|
|
|
|
|
descriptor.value = async function (...args: any[]) {
|
|
|
|
const start = process.hrtime.bigint();
|
|
|
|
const result = await originalMethod.apply(this, args);
|
|
|
|
const end = process.hrtime.bigint();
|
|
|
|
const nanoseconds = end - start;
|
|
|
|
const seconds = Number(nanoseconds) / 1e9;
|
|
|
|
|
|
|
|
const className = target.constructor.name;
|
|
|
|
logger
|
|
|
|
.channel()
|
|
|
|
?.debug(`Exec time [${className}.${propertyKey}]: ${seconds} s`);
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
return descriptor;
|
|
|
|
}
|
|
|
|
|
2024-07-11 12:09:01 +08:00
|
|
|
function catchAndReturn(defaultReturn: any) {
|
|
|
|
return function (
|
|
|
|
target: Object,
|
|
|
|
propertyKey: string,
|
|
|
|
descriptor: TypedPropertyDescriptor<any>
|
|
|
|
) {
|
|
|
|
const originalMethod = descriptor.value;
|
|
|
|
|
|
|
|
descriptor.value = async function (...args: any[]) {
|
|
|
|
try {
|
|
|
|
return await originalMethod.apply(this, args);
|
|
|
|
} catch (error) {
|
2024-07-16 07:55:02 +08:00
|
|
|
if (error instanceof DCLocalServicePortNotSetError) {
|
|
|
|
logger.channel()?.warn(`DC_LOCALSERVICE_PORT is not set in [${propertyKey}]`);
|
|
|
|
return defaultReturn;
|
|
|
|
}
|
|
|
|
|
2024-07-11 12:09:01 +08:00
|
|
|
logger.channel()?.error(`Error in [${propertyKey}]: ${error}`);
|
|
|
|
return defaultReturn;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return descriptor;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-07-08 18:35:34 +08:00
|
|
|
export interface ChatRequest {
|
|
|
|
content: string;
|
|
|
|
model_name: string;
|
|
|
|
api_key: string;
|
|
|
|
api_base: string;
|
|
|
|
parent?: string;
|
|
|
|
context?: string[];
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface ChatResponse {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
|
|
"prompt-hash": string;
|
|
|
|
user: string;
|
|
|
|
date: string;
|
|
|
|
response: string;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
|
|
finish_reason: string;
|
|
|
|
isError: boolean;
|
|
|
|
extra?: object;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface LogData {
|
|
|
|
model: string;
|
|
|
|
messages: object[];
|
|
|
|
parent?: string;
|
|
|
|
timestamp: number;
|
|
|
|
request_tokens: number;
|
|
|
|
response_tokens: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface LogInsertRes {
|
|
|
|
hash?: string;
|
|
|
|
}
|
|
|
|
|
2024-07-08 18:57:15 +08:00
|
|
|
export interface LogDeleteRes {
|
|
|
|
success?: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface ShortLog {
|
|
|
|
hash: string;
|
|
|
|
parent: string | null;
|
|
|
|
user: string;
|
|
|
|
date: string;
|
|
|
|
request: string;
|
|
|
|
responses: string[];
|
|
|
|
context: Array<{
|
|
|
|
content: string;
|
|
|
|
role: string;
|
|
|
|
}>;
|
|
|
|
}
|
2024-07-08 18:35:34 +08:00
|
|
|
|
|
|
|
export async function buildRoleContextsFromFiles(
|
|
|
|
files: string[] | undefined
|
|
|
|
): Promise<object[]> {
|
|
|
|
const contexts: object[] = [];
|
|
|
|
if (!files) {
|
|
|
|
return contexts;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const file of files) {
|
|
|
|
const content = await getFileContent(file);
|
|
|
|
|
|
|
|
if (!content) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
contexts.push({
|
|
|
|
role: "system",
|
|
|
|
content: `<context>${content}</context>`,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return contexts;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class DevChatClient {
|
2024-07-16 07:55:02 +08:00
|
|
|
private baseURL: string | undefined;
|
2024-07-08 18:35:34 +08:00
|
|
|
|
|
|
|
private _cancelMessageToken: CancelTokenSource | null = null;
|
|
|
|
|
2024-07-09 10:42:43 +08:00
|
|
|
static readonly logRawDataSizeLimit = 4 * 1024;
|
2024-07-08 22:18:00 +08:00
|
|
|
|
2024-07-16 07:55:02 +08:00
|
|
|
constructor() {
|
2024-07-08 18:35:34 +08:00
|
|
|
}
|
|
|
|
|
2024-07-09 10:42:43 +08:00
|
|
|
async _get(path: string, config?: any): Promise<AxiosResponse> {
|
2024-07-16 07:55:02 +08:00
|
|
|
if (!this.baseURL) {
|
|
|
|
if (!process.env.DC_LOCALSERVICE_PORT) {
|
|
|
|
logger.channel()?.info("No local service port found.");
|
|
|
|
throw new DCLocalServicePortNotSetError();
|
|
|
|
}
|
2024-11-26 14:01:07 +08:00
|
|
|
logger.channel()?.trace("Using local service port:", process.env.DC_LOCALSERVICE_PORT);
|
2024-07-16 07:55:02 +08:00
|
|
|
const port: number = parseInt(process.env.DC_LOCALSERVICE_PORT || '8008', 10);
|
|
|
|
this.baseURL = `http://localhost:${port}`;
|
|
|
|
}
|
|
|
|
|
2024-07-08 18:35:34 +08:00
|
|
|
try {
|
|
|
|
logger.channel()?.debug(`GET request to ${this.baseURL}${path}`);
|
2024-07-09 10:42:43 +08:00
|
|
|
const response = await axios.get(`${this.baseURL}${path}`, config);
|
2024-07-08 18:35:34 +08:00
|
|
|
return response;
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
async _post(path: string, data: any = undefined): Promise<AxiosResponse> {
|
2024-07-16 07:55:02 +08:00
|
|
|
if (!this.baseURL) {
|
|
|
|
if (!process.env.DC_LOCALSERVICE_PORT) {
|
|
|
|
logger.channel()?.info("No local service port found.");
|
|
|
|
throw new DCLocalServicePortNotSetError();
|
|
|
|
}
|
2024-11-26 14:01:07 +08:00
|
|
|
logger.channel()?.trace("Using local service port:", process.env.DC_LOCALSERVICE_PORT);
|
2024-07-16 07:55:02 +08:00
|
|
|
const port: number = parseInt(process.env.DC_LOCALSERVICE_PORT || '8008', 10);
|
|
|
|
this.baseURL = `http://localhost:${port}`;
|
|
|
|
}
|
|
|
|
|
2024-07-08 18:35:34 +08:00
|
|
|
try {
|
2024-07-09 10:42:43 +08:00
|
|
|
logger.channel()?.debug(`POST request to ${this.baseURL}${path}`);
|
2024-07-08 18:35:34 +08:00
|
|
|
const response = await axios.post(`${this.baseURL}${path}`, data);
|
|
|
|
return response;
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-08 21:18:55 +08:00
|
|
|
@timeThis
|
2024-07-11 12:09:01 +08:00
|
|
|
@catchAndReturn([])
|
|
|
|
async getWorkflowList(): Promise<any[]> {
|
2024-07-09 18:00:11 +08:00
|
|
|
const response = await this._get("/workflows/list");
|
2024-07-08 21:18:55 +08:00
|
|
|
logger
|
|
|
|
.channel()
|
2024-07-11 10:43:51 +08:00
|
|
|
?.trace(
|
2024-07-08 21:18:55 +08:00
|
|
|
`getWorkflowList response data: \n${JSON.stringify(
|
|
|
|
response.data
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
return response.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
@timeThis
|
2024-07-11 12:09:01 +08:00
|
|
|
@catchAndReturn({})
|
2024-07-08 21:18:55 +08:00
|
|
|
async getWorkflowConfig(): Promise<any> {
|
2024-07-09 18:00:11 +08:00
|
|
|
const response = await this._get("/workflows/config");
|
2024-07-08 21:18:55 +08:00
|
|
|
logger
|
|
|
|
.channel()
|
2024-07-11 10:43:51 +08:00
|
|
|
?.trace(
|
2024-07-08 21:18:55 +08:00
|
|
|
`getWorkflowConfig response data: \n${JSON.stringify(
|
|
|
|
response.data
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
return response.data;
|
|
|
|
}
|
2024-07-08 18:35:34 +08:00
|
|
|
|
2024-07-08 19:33:08 +08:00
|
|
|
@timeThis
|
2024-07-11 12:09:01 +08:00
|
|
|
@catchAndReturn(undefined)
|
2024-07-08 19:33:08 +08:00
|
|
|
async updateWorkflows(): Promise<void> {
|
2024-07-09 18:00:11 +08:00
|
|
|
const response = await this._post("/workflows/update");
|
2024-07-08 19:33:08 +08:00
|
|
|
logger
|
|
|
|
.channel()
|
2024-07-11 10:43:51 +08:00
|
|
|
?.trace(
|
2024-07-08 19:33:08 +08:00
|
|
|
`updateWorkflows response data: \n${JSON.stringify(
|
|
|
|
response.data
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-08-26 18:31:03 +08:00
|
|
|
@timeThis
|
|
|
|
@catchAndReturn(undefined)
|
|
|
|
async updateCustomWorkflows(): Promise<void> {
|
|
|
|
const response = await this._post("/workflows/custom_update");
|
|
|
|
logger
|
|
|
|
.channel()
|
|
|
|
?.trace(
|
|
|
|
`updateCustomWorkflows response data: \n${JSON.stringify(
|
|
|
|
response.data
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-07-08 18:35:34 +08:00
|
|
|
@timeThis
|
|
|
|
async message(
|
|
|
|
message: ChatRequest,
|
|
|
|
onData: (data: ChatResponse) => void
|
|
|
|
): Promise<ChatResponse> {
|
2024-07-16 07:55:02 +08:00
|
|
|
if (!this.baseURL) {
|
|
|
|
if (!process.env.DC_LOCALSERVICE_PORT) {
|
|
|
|
logger.channel()?.info("No local service port found.");
|
|
|
|
}
|
2024-11-26 14:01:07 +08:00
|
|
|
logger.channel()?.trace("Using local service port:", process.env.DC_LOCALSERVICE_PORT);
|
2024-07-16 07:55:02 +08:00
|
|
|
const port: number = parseInt(process.env.DC_LOCALSERVICE_PORT || '8008', 10);
|
|
|
|
this.baseURL = `http://localhost:${port}`;
|
|
|
|
}
|
|
|
|
|
2024-07-08 18:35:34 +08:00
|
|
|
this._cancelMessageToken = axios.CancelToken.source();
|
|
|
|
const workspace = UiUtilWrapper.workspaceFoldersFirstPath();
|
|
|
|
// const workspace = undefined;
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
...message,
|
|
|
|
workspace: workspace,
|
|
|
|
};
|
|
|
|
|
|
|
|
return new Promise<ChatResponse>(async (resolve, reject) => {
|
|
|
|
try {
|
|
|
|
const response = await axios.post(
|
|
|
|
`${this.baseURL}/message/msg`,
|
|
|
|
data,
|
|
|
|
{
|
|
|
|
responseType: "stream",
|
|
|
|
cancelToken: this._cancelMessageToken!.token,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
const chatRes: ChatResponse = {
|
2024-07-11 10:43:51 +08:00
|
|
|
"prompt-hash": "", // prompt-hash is not in chatting response, it is created in the later insertLog()
|
2024-07-08 18:35:34 +08:00
|
|
|
user: "",
|
|
|
|
date: "",
|
|
|
|
response: "",
|
|
|
|
finish_reason: "",
|
|
|
|
isError: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
response.data.on("data", (chunk) => {
|
|
|
|
const chunkData = JSON.parse(chunk.toString());
|
|
|
|
|
|
|
|
if (chatRes.user === "") {
|
|
|
|
chatRes.user = chunkData["user"];
|
|
|
|
}
|
|
|
|
if (chatRes.date === "") {
|
|
|
|
chatRes.date = chunkData["date"];
|
|
|
|
}
|
|
|
|
chatRes.finish_reason = chunkData["finish_reason"];
|
|
|
|
if (chatRes.finish_reason === "should_run_workflow") {
|
|
|
|
chatRes.extra = chunkData["extra"];
|
2024-07-11 12:09:01 +08:00
|
|
|
logger.channel()?.debug("should run workflow via cli.");
|
2024-07-08 18:35:34 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
chatRes.isError = chunkData["isError"];
|
|
|
|
|
|
|
|
chatRes.response += chunkData["content"];
|
|
|
|
onData(chatRes);
|
|
|
|
});
|
|
|
|
|
|
|
|
response.data.on("end", () => {
|
|
|
|
resolve(chatRes); // Resolve the promise with chatRes when the stream ends
|
|
|
|
});
|
|
|
|
|
|
|
|
response.data.on("error", (error) => {
|
|
|
|
logger.channel()?.error("Streaming error:", error);
|
2024-07-11 12:09:01 +08:00
|
|
|
chatRes.isError = true;
|
|
|
|
chatRes.response += `\n${error}`;
|
|
|
|
resolve(chatRes); // handle error by resolving the promise
|
2024-07-08 18:35:34 +08:00
|
|
|
});
|
|
|
|
} catch (error) {
|
2024-07-11 12:09:01 +08:00
|
|
|
const errorRes: ChatResponse = {
|
|
|
|
"prompt-hash": "",
|
|
|
|
user: "",
|
|
|
|
date: "",
|
|
|
|
response: `${error}`,
|
|
|
|
finish_reason: "",
|
|
|
|
isError: true,
|
|
|
|
};
|
|
|
|
resolve(errorRes); // handle error by resolving the promise using an errorRes
|
2024-07-08 18:35:34 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
cancelMessage(): void {
|
|
|
|
if (this._cancelMessageToken) {
|
|
|
|
this._cancelMessageToken.cancel(
|
|
|
|
"Message request cancelled by user"
|
|
|
|
);
|
|
|
|
this._cancelMessageToken = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Insert a message log.
|
|
|
|
*
|
|
|
|
* @param logData - The log data to be inserted.
|
|
|
|
* @returns A tuple of inserted hash and error message.
|
|
|
|
*/
|
|
|
|
@timeThis
|
2024-07-11 12:09:01 +08:00
|
|
|
@catchAndReturn({ hash: "" })
|
2024-07-08 18:35:34 +08:00
|
|
|
async insertLog(logData: LogData): Promise<LogInsertRes> {
|
2024-07-08 22:18:00 +08:00
|
|
|
let body = {
|
2024-07-08 18:35:34 +08:00
|
|
|
workspace: UiUtilWrapper.workspaceFoldersFirstPath(),
|
|
|
|
};
|
2024-07-08 22:18:00 +08:00
|
|
|
|
|
|
|
const jsondata = JSON.stringify(logData);
|
|
|
|
let filepath = "";
|
|
|
|
|
|
|
|
if (jsondata.length <= DevChatClient.logRawDataSizeLimit) {
|
|
|
|
// Use json data directly
|
|
|
|
body["jsondata"] = jsondata;
|
|
|
|
} else {
|
|
|
|
// Write json data to a temp file
|
|
|
|
const tempDir = os.tmpdir();
|
|
|
|
const tempFile = path.join(tempDir, "devchat_log_insert.json");
|
|
|
|
await fs.promises.writeFile(tempFile, jsondata);
|
|
|
|
filepath = tempFile;
|
|
|
|
body["filepath"] = filepath;
|
|
|
|
}
|
|
|
|
|
|
|
|
const response = await this._post("/logs/insert", body);
|
2024-07-08 18:35:34 +08:00
|
|
|
logger
|
|
|
|
.channel()
|
2024-07-11 10:43:51 +08:00
|
|
|
?.trace(
|
2024-07-08 18:35:34 +08:00
|
|
|
`insertLog response data: ${JSON.stringify(
|
|
|
|
response.data
|
|
|
|
)}, ${typeof response.data}}`
|
|
|
|
);
|
2024-07-11 12:09:01 +08:00
|
|
|
|
2024-07-08 22:18:00 +08:00
|
|
|
// Clean up temp file
|
|
|
|
if (filepath) {
|
|
|
|
try {
|
|
|
|
await fs.promises.unlink(filepath);
|
|
|
|
} catch (error) {
|
2024-07-11 12:09:01 +08:00
|
|
|
logger
|
|
|
|
.channel()
|
|
|
|
?.error(`Failed to delete temp file ${filepath}: ${error}`);
|
2024-07-08 22:18:00 +08:00
|
|
|
}
|
|
|
|
}
|
2024-07-08 18:35:34 +08:00
|
|
|
|
|
|
|
const res: LogInsertRes = {
|
|
|
|
hash: response.data["hash"],
|
|
|
|
};
|
|
|
|
return res;
|
|
|
|
}
|
2024-07-08 18:57:15 +08:00
|
|
|
|
|
|
|
@timeThis
|
2024-07-11 12:09:01 +08:00
|
|
|
@catchAndReturn({ success: false })
|
2024-07-08 18:57:15 +08:00
|
|
|
async deleteLog(logHash: string): Promise<LogDeleteRes> {
|
|
|
|
const data = {
|
|
|
|
workspace: UiUtilWrapper.workspaceFoldersFirstPath(),
|
|
|
|
hash: logHash,
|
|
|
|
};
|
|
|
|
const response = await this._post("/logs/delete", data);
|
|
|
|
logger
|
|
|
|
.channel()
|
2024-07-11 10:43:51 +08:00
|
|
|
?.trace(
|
2024-07-08 18:57:15 +08:00
|
|
|
`deleteLog response data: ${JSON.stringify(
|
|
|
|
response.data
|
|
|
|
)}, ${typeof response.data}}`
|
|
|
|
);
|
2024-07-11 12:09:01 +08:00
|
|
|
|
2024-07-08 18:57:15 +08:00
|
|
|
const res: LogDeleteRes = {
|
|
|
|
success: response.data["success"],
|
|
|
|
};
|
|
|
|
return res;
|
2024-07-11 12:09:01 +08:00
|
|
|
}
|
2024-07-08 18:57:15 +08:00
|
|
|
|
2024-07-08 19:04:16 +08:00
|
|
|
@timeThis
|
2024-07-11 12:09:01 +08:00
|
|
|
@catchAndReturn([])
|
2024-07-08 19:04:16 +08:00
|
|
|
async getTopicLogs(
|
|
|
|
topicRootHash: string,
|
|
|
|
limit: number,
|
|
|
|
offset: number
|
|
|
|
): Promise<ShortLog[]> {
|
|
|
|
const data = {
|
|
|
|
limit: limit,
|
|
|
|
offset: offset,
|
|
|
|
workspace: UiUtilWrapper.workspaceFoldersFirstPath(),
|
|
|
|
};
|
2024-07-11 12:09:01 +08:00
|
|
|
const response = await this._get(`/topics/${topicRootHash}/logs`, {
|
|
|
|
params: data,
|
|
|
|
});
|
2024-07-08 19:04:16 +08:00
|
|
|
|
|
|
|
const logs: ShortLog[] = response.data;
|
|
|
|
logs.reverse();
|
|
|
|
|
|
|
|
logger
|
|
|
|
.channel()
|
2024-07-11 10:43:51 +08:00
|
|
|
?.trace(`getTopicLogs response data: ${JSON.stringify(logs)}`);
|
2024-07-08 19:04:16 +08:00
|
|
|
|
|
|
|
return logs;
|
|
|
|
}
|
|
|
|
|
2024-07-08 19:12:06 +08:00
|
|
|
@timeThis
|
2024-07-11 12:09:01 +08:00
|
|
|
@catchAndReturn([])
|
2024-07-08 22:20:37 +08:00
|
|
|
async getTopics(limit: number, offset: number): Promise<any[]> {
|
2024-07-08 19:12:06 +08:00
|
|
|
const data = {
|
|
|
|
limit: limit,
|
|
|
|
offset: offset,
|
|
|
|
workspace: UiUtilWrapper.workspaceFoldersFirstPath(),
|
|
|
|
};
|
2024-07-09 10:42:43 +08:00
|
|
|
const response = await this._get(`/topics`, {
|
2024-07-08 19:12:06 +08:00
|
|
|
params: data,
|
|
|
|
});
|
|
|
|
|
|
|
|
const topics: any[] = response.data;
|
|
|
|
topics.reverse();
|
|
|
|
|
|
|
|
logger
|
|
|
|
.channel()
|
2024-07-11 10:43:51 +08:00
|
|
|
?.trace(`getTopics response data: ${JSON.stringify(topics)}`);
|
2024-07-08 19:12:06 +08:00
|
|
|
|
|
|
|
return topics;
|
|
|
|
}
|
|
|
|
|
|
|
|
@timeThis
|
2024-07-11 12:09:01 +08:00
|
|
|
@catchAndReturn(undefined)
|
2024-07-08 22:20:37 +08:00
|
|
|
async deleteTopic(topicRootHash: string): Promise<void> {
|
2024-07-08 19:12:06 +08:00
|
|
|
const data = {
|
|
|
|
topic_hash: topicRootHash,
|
|
|
|
workspace: UiUtilWrapper.workspaceFoldersFirstPath(),
|
|
|
|
};
|
2024-07-08 22:20:37 +08:00
|
|
|
|
2024-07-08 19:12:06 +08:00
|
|
|
const response = await this._post("/topics/delete", data);
|
|
|
|
|
|
|
|
logger
|
|
|
|
.channel()
|
2024-07-11 10:43:51 +08:00
|
|
|
?.trace(
|
2024-07-08 22:20:37 +08:00
|
|
|
`deleteTopic response data: ${JSON.stringify(response.data)}`
|
|
|
|
);
|
2024-07-08 19:12:06 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2024-07-08 19:04:16 +08:00
|
|
|
|
2024-07-08 18:35:34 +08:00
|
|
|
stopAllRequest(): void {
|
|
|
|
this.cancelMessage();
|
|
|
|
// add other requests here if needed
|
|
|
|
}
|
2024-07-16 07:55:02 +08:00
|
|
|
}
|