View Javadoc
1   /*
2    * Copyright 2012-2025 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.entity;
17  
18  import java.time.LocalDateTime;
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.UUID;
22  import java.util.concurrent.CopyOnWriteArrayList;
23  
24  /**
25   * Represents a chat session containing conversation history.
26   *
27   * @author FessProject
28   */
29  public class ChatSession {
30  
31      /** The unique session identifier. */
32      private String sessionId;
33  
34      /** The user ID associated with this session. */
35      private String userId;
36  
37      /** The timestamp when the session was created. */
38      private LocalDateTime createdAt;
39  
40      /** The timestamp when the session was last accessed. */
41      private volatile LocalDateTime lastAccessedAt;
42  
43      /** The list of messages in this session. */
44      private List<ChatMessage> messages;
45  
46      /** Lock object for thread-safe message operations. */
47      private final Object messagesLock = new Object();
48  
49      /**
50       * Default constructor.
51       */
52      public ChatSession() {
53          this.sessionId = UUID.randomUUID().toString();
54          this.createdAt = LocalDateTime.now();
55          this.lastAccessedAt = this.createdAt;
56          this.messages = new CopyOnWriteArrayList<>();
57      }
58  
59      /**
60       * Creates a new chat session for the specified user.
61       *
62       * @param userId the user ID
63       */
64      public ChatSession(final String userId) {
65          this();
66          this.userId = userId;
67      }
68  
69      /**
70       * Gets the session ID.
71       *
72       * @return the session ID
73       */
74      public String getSessionId() {
75          return sessionId;
76      }
77  
78      /**
79       * Sets the session ID.
80       *
81       * @param sessionId the session ID
82       */
83      public void setSessionId(final String sessionId) {
84          this.sessionId = sessionId;
85      }
86  
87      /**
88       * Gets the user ID.
89       *
90       * @return the user ID
91       */
92      public String getUserId() {
93          return userId;
94      }
95  
96      /**
97       * Sets the user ID.
98       *
99       * @param userId the user ID
100      */
101     public void setUserId(final String userId) {
102         this.userId = userId;
103     }
104 
105     /**
106      * Gets the creation timestamp.
107      *
108      * @return the creation timestamp
109      */
110     public LocalDateTime getCreatedAt() {
111         return createdAt;
112     }
113 
114     /**
115      * Sets the creation timestamp.
116      *
117      * @param createdAt the creation timestamp
118      */
119     public void setCreatedAt(final LocalDateTime createdAt) {
120         this.createdAt = createdAt;
121     }
122 
123     /**
124      * Gets the last accessed timestamp.
125      *
126      * @return the last accessed timestamp
127      */
128     public LocalDateTime getLastAccessedAt() {
129         return lastAccessedAt;
130     }
131 
132     /**
133      * Sets the last accessed timestamp.
134      *
135      * @param lastAccessedAt the last accessed timestamp
136      */
137     public void setLastAccessedAt(final LocalDateTime lastAccessedAt) {
138         this.lastAccessedAt = lastAccessedAt;
139     }
140 
141     /**
142      * Returns a copy of the message list in this session.
143      *
144      * @return a new list containing all messages
145      */
146     public List<ChatMessage> getMessages() {
147         synchronized (messagesLock) {
148             if (messages == null) {
149                 return new ArrayList<>();
150             }
151             return new ArrayList<>(messages);
152         }
153     }
154 
155     /**
156      * Sets the message list for this session.
157      *
158      * @param messages the messages to set
159      */
160     public void setMessages(final List<ChatMessage> messages) {
161         synchronized (messagesLock) {
162             if (messages == null) {
163                 this.messages = null;
164             } else if (messages instanceof CopyOnWriteArrayList) {
165                 this.messages = messages;
166             } else {
167                 this.messages = new CopyOnWriteArrayList<>(messages);
168             }
169         }
170     }
171 
172     /**
173      * Adds a message to this session and updates the last accessed timestamp.
174      *
175      * @param message the message to add
176      */
177     public void addMessage(final ChatMessage message) {
178         synchronized (messagesLock) {
179             if (messages == null) {
180                 messages = new CopyOnWriteArrayList<>();
181             }
182             messages.add(message);
183         }
184         this.lastAccessedAt = LocalDateTime.now();
185     }
186 
187     /**
188      * Adds a user message to this session.
189      *
190      * @param content the message content
191      */
192     public void addUserMessage(final String content) {
193         addMessage(ChatMessage.userMessage(content));
194     }
195 
196     /**
197      * Adds an assistant message to this session.
198      *
199      * @param content the message content
200      */
201     public void addAssistantMessage(final String content) {
202         addMessage(ChatMessage.assistantMessage(content));
203     }
204 
205     /**
206      * Updates the last accessed timestamp to the current time.
207      */
208     public void touch() {
209         this.lastAccessedAt = LocalDateTime.now();
210     }
211 
212     /**
213      * Returns the number of messages in this session.
214      *
215      * @return the message count
216      */
217     public int getMessageCount() {
218         synchronized (messagesLock) {
219             return messages != null ? messages.size() : 0;
220         }
221     }
222 
223     /**
224      * Clears all messages in this session and updates the last accessed timestamp.
225      */
226     public void clearMessages() {
227         synchronized (messagesLock) {
228             if (messages != null) {
229                 messages.clear();
230             }
231         }
232         this.lastAccessedAt = LocalDateTime.now();
233     }
234 
235     /**
236      * Trims the message history to keep only the most recent messages.
237      *
238      * @param maxMessages the maximum number of messages to retain
239      */
240     public void trimHistory(final int maxMessages) {
241         synchronized (messagesLock) {
242             if (messages != null && messages.size() > maxMessages) {
243                 int start = messages.size() - maxMessages;
244                 // Ensure trimmed history starts with a user message, not an assistant message
245                 if (start < messages.size() && ChatMessage.ROLE_ASSISTANT.equals(messages.get(start).getRole())) {
246                     start = Math.max(0, start - 1);
247                 }
248                 final List<ChatMessage> trimmed = new ArrayList<>(messages.subList(start, messages.size()));
249                 messages.clear();
250                 messages.addAll(trimmed);
251             }
252         }
253     }
254 }