File size: 3,811 Bytes
5fc68b0
 
 
 
 
 
 
 
 
 
 
 
5c221d7
 
5fc68b0
136f9cf
5fc68b0
 
136f9cf
5fc68b0
136f9cf
5fc68b0
 
5c221d7
5fc68b0
 
136f9cf
 
 
 
 
 
 
5fc68b0
 
 
 
 
 
 
 
136f9cf
5fc68b0
 
 
 
 
136f9cf
 
5fc68b0
 
 
 
 
 
 
 
 
 
 
 
620331c
136f9cf
5fc68b0
 
 
 
 
 
 
 
620331c
5fc68b0
 
5c221d7
 
 
 
 
5fc68b0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { BaseChatMessageHistory } from "@langchain/core/chat_history";
import Dexie from "dexie";
import { IChatSession } from "./types";
import { ConfigManager } from "@/lib/config/manager";
import {
  mapStoredMessageToChatMessage,
  mapChatMessagesToStoredMessages,
  BaseMessage,
  AIMessage,
  HumanMessage,
} from "@langchain/core/messages";
import { IConfig, CHAT_MODELS, EMBEDDING_MODELS } from "@/lib/config/types";
import { ChatCompletionReasoningEffort } from "openai/resources/chat/completions";
import { PROVIDERS } from "@/lib/config/types";

// Create a singleton instance of ChatHistoryDB
export class ChatHistoryDB extends Dexie {
  sessions!: Dexie.Table<IChatSession, string>;
  private static instance: ChatHistoryDB | null = null;

  private constructor() {
    super("chat_history");
    this.version(1).stores({
      sessions: "id, title, createdAt, updatedAt, model, embedding_model, enabled_tools, messages, reasoningEffort",
    });
  }

  public static getInstance(): ChatHistoryDB {
    if (!ChatHistoryDB.instance) {
      ChatHistoryDB.instance = new ChatHistoryDB();
    }
    return ChatHistoryDB.instance;
  }
}

export class DexieChatMemory extends BaseChatMessageHistory {
  db: ChatHistoryDB;
  lc_namespace = ["langchain", "stores", "message", "dexie"];
  sessionId: string;
  chatHistory!: IChatSession | undefined;
  config!: IConfig;
  configManager: ConfigManager;
  initialized: boolean = false;

  constructor(sessionId: string) {
    super();
    this.sessionId = sessionId;
    this.db = ChatHistoryDB.getInstance();
    this.configManager = ConfigManager.getInstance();
  }

  async initialize() {
    if (this.initialized) return;
    
    this.config = await this.configManager.getConfig();
    this.chatHistory = await this.db.sessions.get(this.sessionId);
    
    if (!this.chatHistory) {
      const chatModel = CHAT_MODELS.find((m) => m.model === this.config.default_chat_model);
      const embeddingModel = EMBEDDING_MODELS.find((m) => m.model === this.config.default_embedding_model);
      
      if (!chatModel) {
        throw new Error("Chat or embedding models are not configured.");
      }

      this.chatHistory = {
        id: this.sessionId,
        name: "New Chat",
        createdAt: Date.now(),
        updatedAt: Date.now(),
        model: chatModel.model,
        embedding_model: embeddingModel?.model || '',
        enabled_tools: [],
        messages: [],
        reasoningEffort: chatModel.isReasoning 
          ? (chatModel.provider === PROVIDERS.anthropic 
              ? "disabled" as ChatCompletionReasoningEffort 
              : "low" as ChatCompletionReasoningEffort)
          : null as ChatCompletionReasoningEffort,
      };

      await this.db.sessions.put(this.chatHistory);
    }
    
    this.initialized = true;
  }

  private async updateSession() {
    if (!this.chatHistory) return;
    this.chatHistory.updatedAt = Date.now();
    await this.db.sessions.put(this.chatHistory);
  }

  async getMessages(): Promise<BaseMessage[]> {
    await this.initialize();
    return this.chatHistory?.messages.map(mapStoredMessageToChatMessage) || [];
  }

  async addMessage(message: BaseMessage): Promise<void> {
    await this.initialize();
    if (!this.chatHistory) return;
    const storedMessage = mapChatMessagesToStoredMessages([message]);
    this.chatHistory.messages.push(storedMessage[0]);
    await this.updateSession();
  }

  async addUserMessage(message: string): Promise<void> {
    await this.addMessage(new HumanMessage(message));
  }

  async addAIChatMessage(message: string): Promise<void> {
    await this.addMessage(new AIMessage(message));
  }

  async clear(): Promise<void> {
    this.chatHistory = undefined;
    this.initialized = false;
    await this.db.sessions.delete(this.sessionId);
  }
}