Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 13 additions & 50 deletions GPTutor-Frontend/src/entity/GPT/ChatGpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { sig } from "dignals";
import { ChatGptFree } from "$/entity/GPT/ChatGptFree";
import { ChatGptLesson } from "$/entity/GPT/ChatGptLesson";
import { GptHistoryDialogs } from "$/entity/GPT/GptHistoryDialogs";
import { LessonItem, lessonsController, ModeType } from "$/entity/lessons";
import { LessonItem, ModeType } from "$/entity/lessons";
import { ChatGptTemplate } from "$/entity/GPT/ChatGptTemplate";
import { ChatGptInterview } from "$/entity/GPT/ChatGptInterview";
import { ChatGptLeetCode } from "$/entity/GPT/ChatGptLeetCode";
import { interviews } from "$/entity/interview";
import { ChatGptTrainer } from "$/entity/GPT/ChatGptTrainer";
import { VkStorageService } from "$/services/VkStorageService";
import { ChatGptAnecdote } from "$/entity/GPT/ChatGptAnecdote";
import { DialogRouterService } from "./DialogRouterService";
import { ChatStateManager } from "./ChatStateManager";

export class ChatGpt {
storageService = new VkStorageService();
Expand Down Expand Up @@ -46,37 +47,20 @@ export class ChatGpt {
currentChatGpt$ = sig<ChatGptTemplate>(this.chatGptFree);

moveToFreeChat = (goToChat: () => void) => {
lessonsController.clearLesson();
this.chatGptFree.currentHistory = null;

ChatStateManager.clearLessonState();
ChatStateManager.initializeFreeChat(this.chatGptFree);
this.currentChatGpt$.set(this.chatGptFree);

this.chatGptFree.clearMessages();
this.chatGptFree.abortSend();

this.chatGptFree.resetSystemMessage();

goToChat();
};

moveToLessonChat(lesson: LessonItem, goToChatLesson: () => void) {
this.chatGptLesson.clearMessages();
this.chatGptLesson.resetSystemMessage();
this.chatGptLesson.currentHistory = null;

ChatStateManager.initializeLessonChat(this.chatGptLesson, lesson);
this.currentChatGpt$.set(this.chatGptLesson);
this.chatGptLesson.setInitialSystemMessage(
lessonsController.currentChapter.get()?.systemMessage
);

lessonsController.setCurrentLesson(lesson.id);

goToChatLesson();
}

moveToInterviewChat(interviewType: string, goToChatInterview: () => void) {
interviews.setCurrentInterview(interviewType as ModeType);
this.chatGptInterview.messages$.set([]);
ChatStateManager.initializeInterviewChat(this.chatGptInterview, interviewType);
goToChatInterview();
}

Expand All @@ -90,33 +74,12 @@ export class ChatGpt {
const dialog = this.history.getDialogById(id);
if (!dialog) return;

if (dialog.type === "Free") {
this.currentChatGpt$.set(this.chatGptFree);
await this.chatGptFree.restoreDialogFromHistory(dialog, goToChatFree);
return;
}
if (dialog.type === ModeType.LeetCode) {
this.currentChatGpt$.set(this.chatGptLeetCode);
await this.chatGptLeetCode.restoreDialogFromHistory(
dialog,
goToChatLeetCode
);
return;
}

if (dialog.type.includes("INTERVIEW")) {
this.currentChatGpt$.set(this.chatGptInterview);
await this.chatGptInterview.restoreDialogFromHistory(
dialog,
goToChatInterview
);
}

if (dialog.type && dialog.lessonName) {
this.currentChatGpt$.set(this.chatGptLesson);
await this.chatGptLesson.restoreDialogFromHistory(dialog, goToChatLesson);
return;
}
await DialogRouterService.routeDialog(this, dialog, {
goToChatFree,
goToChatLesson,
goToChatInterview,
goToChatLeetCode
});
}

getCurrentChatGpt = () => this.currentChatGpt$.get();
Expand Down
44 changes: 7 additions & 37 deletions GPTutor-Frontend/src/entity/GPT/ChatGptTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { SubscriptionGPT } from "$/entity/GPT/SubscriptionGPT";
import { getBannerName } from "$/entity/history/utils";
import { gptModels } from "$/entity/GPT/GptModels";
import { userInfo } from "$/entity/user/UserInfo";
import { DialogRestoreService } from "./DialogRestoreService";

const initialSystemContent = `ะขะตะฑั ะทะพะฒัƒั‚ Deep.GPT, ะฑัƒะดัŒ ะฟะพะปะตะทะฝั‹ะผ ะฟะพะผะพั‰ะฝะธะบะพะผ.`;

Expand Down Expand Up @@ -355,42 +356,11 @@ export abstract class ChatGptTemplate {
lessonsController.clearLesson();
}

//todo ั€ะตั„ะฐะบั‚ะพั€ะธะฝะณ
async restoreDialogFromHistory(dialog: History, goToChat: () => void) {
this.closeDelay();

this.currentHistory = dialog;

const messages = await this.getMessages$.run(dialog.id);

if (this.getMessages$.error.get()) {
return snackbarNotify.notify({
type: "error",
message: "ะžัˆะธะฑะบะฐ ะฟั€ะธ ะฟะตั€ะตั…ะพะดะต ะฒ ะดะธะฟะปะพะณ",
});
}

await this.prepareDialog(dialog);

this.initialSystemContent = dialog.systemMessage;
this.systemMessage = new GptMessage(dialog.systemMessage, GPTRoles.system);

this.messages$.set(
messages.map((message) => {
const gptMessage = new GptMessage(
message.content,
message.role as GPTRoles,
false,
message.error
);

gptMessage.failedModeration$.set(message.isFailedModeration);

return gptMessage;
})
);

this.checkOnRunOutOfMessages();
goToChat();
/**
* Restore dialog from history using DialogRestoreService
* Refactored to improve separation of concerns and reduce complexity
*/
async restoreDialogFromHistory(dialog: History, goToChat: () => void): Promise<void> {
return DialogRestoreService.restoreDialog(this, dialog, goToChat);
}
}
62 changes: 62 additions & 0 deletions GPTutor-Frontend/src/entity/GPT/ChatStateManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ChatGptTemplate } from "./ChatGptTemplate";
import { lessonsController, LessonItem } from "$/entity/lessons";
import { interviews } from "$/entity/interview";
import type { ModeType } from "$/entity/lessons";

/**
* Service for managing chat state transitions and initialization
* Extracted from ChatGpt class to reduce duplication and improve maintainability
*/
export class ChatStateManager {
/**
* Initialize free chat state
*/
static initializeFreeChat(chatInstance: ChatGptTemplate): void {
chatInstance.currentHistory = null;
chatInstance.clearMessages();
chatInstance.abortSend();
chatInstance.resetSystemMessage();
}

/**
* Initialize lesson chat state
*/
static initializeLessonChat(
chatInstance: ChatGptTemplate,
lesson: LessonItem
): void {
chatInstance.clearMessages();
chatInstance.resetSystemMessage();
chatInstance.currentHistory = null;

const systemMessage = lessonsController.currentChapter.get()?.systemMessage;
chatInstance.setInitialSystemMessage(systemMessage);
lessonsController.setCurrentLesson(lesson.id);
}

/**
* Initialize interview chat state
*/
static initializeInterviewChat(
chatInstance: ChatGptTemplate,
interviewType: string
): void {
interviews.setCurrentInterview(interviewType as ModeType);
chatInstance.messages$.set([]);
}

/**
* Clear all lesson-related state
*/
static clearLessonState(): void {
lessonsController.clearLesson();
}

/**
* Clear all chapter-related state
*/
static clearChapterState(): void {
lessonsController.clearChapter();
lessonsController.clearLesson();
}
}
89 changes: 89 additions & 0 deletions GPTutor-Frontend/src/entity/GPT/DialogRestoreService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { History } from "$/entity/history";
import { ModeType } from "$/entity/lessons";
import { ChatGptTemplate } from "./ChatGptTemplate";

/**
* Service for handling dialog restoration logic
* Extracted from ChatGptTemplate to improve separation of concerns
*/
export class DialogRestoreService {
static async restoreDialog(
chatGptInstance: ChatGptTemplate,
dialog: History,
goToChat: () => void
): Promise<void> {
chatGptInstance.closeDelay();
chatGptInstance.currentHistory = dialog;

const messages = await chatGptInstance.getMessages$.run(dialog.id);

if (chatGptInstance.getMessages$.error.get()) {
const { snackbarNotify } = await import("$/entity/notify");
return snackbarNotify.notify({
type: "error",
message: "ะžัˆะธะฑะบะฐ ะฟั€ะธ ะฟะตั€ะตั…ะพะดะต ะฒ ะดะธะฟะปะพะณ",
});
}

await this.prepareDialog(dialog);
this.setSystemMessage(chatGptInstance, dialog);
this.restoreMessages(chatGptInstance, messages);

chatGptInstance.checkOnRunOutOfMessages();
goToChat();
}

private static async prepareDialog(dialog: History): Promise<void> {
if (dialog.type === ModeType.LeetCode) {
const { leetCode } = await import("$/entity/leetCode/LeetCode");
await leetCode.loadDetailProblem(dialog.lessonName);
return;
}

if (dialog.type.includes("INTERVIEW")) {
const { interviews } = await import("$/entity/interview");
interviews.setCurrentInterview(dialog.type as ModeType);
return;
}

if (dialog.lessonName && dialog.type) {
const { lessonsController } = await import("$/entity/lessons");
lessonsController.setCurrentChapter(dialog.type as ModeType);
lessonsController.setCurrentLessonByName(dialog.lessonName);
return;
}

const { lessonsController } = await import("$/entity/lessons");
lessonsController.clearChapter();
lessonsController.clearLesson();
}

private static setSystemMessage(chatGptInstance: ChatGptTemplate, dialog: History): void {
// Import at top level to avoid circular dependency issues
const { GptMessage } = require("./GptMessage");
const { GPTRoles } = require("./types");

chatGptInstance.initialSystemContent = dialog.systemMessage;
chatGptInstance.systemMessage = new GptMessage(dialog.systemMessage, GPTRoles.system);
}

private static restoreMessages(chatGptInstance: ChatGptTemplate, messages: any[]): void {
// Import at top level to avoid circular dependency issues
const { GptMessage } = require("./GptMessage");
const { GPTRoles } = require("./types");

const gptMessages = messages.map((message) => {
const gptMessage = new GptMessage(
message.content,
message.role as typeof GPTRoles[keyof typeof GPTRoles],
false,
message.error
);

gptMessage.failedModeration$.set(message.isFailedModeration);
return gptMessage;
});

chatGptInstance.messages$.set(gptMessages);
}
}
63 changes: 63 additions & 0 deletions GPTutor-Frontend/src/entity/GPT/DialogRouterService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { History } from "$/entity/history";
import { ModeType } from "$/entity/lessons";
import type { ChatGpt } from "./ChatGpt";

/**
* Service for handling dialog routing logic
* Extracted from ChatGpt class to improve maintainability
*/
export class DialogRouterService {
/**
* Routes dialog restoration to the appropriate chat type based on dialog properties
*/
static async routeDialog(
chatGpt: ChatGpt,
dialog: History,
navigationCallbacks: {
goToChatFree: () => void;
goToChatLesson: () => void;
goToChatInterview: () => void;
goToChatLeetCode: () => void;
}
): Promise<void> {
const { goToChatFree, goToChatLesson, goToChatInterview, goToChatLeetCode } = navigationCallbacks;

// Route to Free Chat
if (dialog.type === "Free") {
chatGpt.currentChatGpt$.set(chatGpt.chatGptFree);
await chatGpt.chatGptFree.restoreDialogFromHistory(dialog, goToChatFree);
return;
}

// Route to LeetCode Chat
if (dialog.type === ModeType.LeetCode) {
chatGpt.currentChatGpt$.set(chatGpt.chatGptLeetCode);
await chatGpt.chatGptLeetCode.restoreDialogFromHistory(dialog, goToChatLeetCode);
return;
}

// Route to Interview Chat
if (dialog.type.includes("INTERVIEW")) {
chatGpt.currentChatGpt$.set(chatGpt.chatGptInterview);
await chatGpt.chatGptInterview.restoreDialogFromHistory(dialog, goToChatInterview);
return;
}

// Route to Lesson Chat
if (dialog.type && dialog.lessonName) {
chatGpt.currentChatGpt$.set(chatGpt.chatGptLesson);
await chatGpt.chatGptLesson.restoreDialogFromHistory(dialog, goToChatLesson);
return;
}
}

/**
* Get the appropriate chat instance for a given dialog type
*/
static getChatInstanceForDialogType(chatGpt: ChatGpt, dialogType: string) {
if (dialogType === "Free") return chatGpt.chatGptFree;
if (dialogType === ModeType.LeetCode) return chatGpt.chatGptLeetCode;
if (dialogType.includes("INTERVIEW")) return chatGpt.chatGptInterview;
return chatGpt.chatGptLesson; // default for lesson dialogs
}
}
3 changes: 3 additions & 0 deletions GPTutor-Frontend/src/entity/GPT/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export * from "./ChatGpt";
export * from "./GptMessage";
export * from "./types";
export * from "./DialogRestoreService";
export * from "./DialogRouterService";
export * from "./ChatStateManager";