Skip to content

Commit 3481315

Browse files
authored
🐛 Fix session persistence, when request contains <think> (#10)
1 parent 93d79eb commit 3481315

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

app/server/chat.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,18 @@ async def create_chat_completion(
109109
raise
110110

111111
# Format and clean the output
112-
model_output = GeminiClientWrapper.extract_output(response)
112+
model_output = GeminiClientWrapper.extract_output(response, include_thoughts=True)
113113
stored_output = GeminiClientWrapper.extract_output(response, include_thoughts=False)
114114

115115
# After cleaning, persist the conversation
116116
try:
117117
last_message = Message(role="assistant", content=stored_output)
118+
cleaned_history = db.clean_assistant_messages(request.messages)
118119
conv = ConversationInStore(
119120
model=model.model_name,
120121
client_id=client.id,
121122
metadata=session.metadata,
122-
messages=[*request.messages, last_message],
123+
messages=[*cleaned_history, last_message],
123124
)
124125
key = db.store(conv)
125126
logger.debug(f"Conversation saved to LMDB with key: {key}")

app/services/lmdb.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@
1111
from ..models import ConversationInStore, Message
1212
from ..utils import g_config
1313
from ..utils.singleton import Singleton
14+
import re
1415

16+
def _normalize_content(content: str) -> str:
17+
"""Remove <think>...</think> tags and strip whitespace from content."""
18+
# Remove think tags
19+
cleaned_content = re.sub(r"<think>.*?</think>\n?", "", content, flags=re.DOTALL)
20+
# Strip leading/trailing whitespace
21+
return cleaned_content.strip()
1522

1623
def hash_message(message: Message) -> str:
1724
"""Generate a hash for a single message."""
@@ -171,6 +178,23 @@ def get(self, key: str) -> Optional[ConversationInStore]:
171178
logger.error(f"Failed to retrieve messages for key {key}: {e}")
172179
return None
173180

181+
def clean_assistant_messages(self, messages: List[Message]) -> List[Message]:
182+
"""Create a new list of messages with assistant content cleaned."""
183+
cleaned_messages = []
184+
for msg in messages:
185+
if msg.role == "assistant" and isinstance(msg.content, str):
186+
# Create a new Message object with cleaned content
187+
normalized_content = _normalize_content(msg.content)
188+
# Only create a new object if content actually changed
189+
if normalized_content != msg.content:
190+
cleaned_msg = Message(role=msg.role, content=normalized_content, name=msg.name)
191+
cleaned_messages.append(cleaned_msg)
192+
else:
193+
cleaned_messages.append(msg)
194+
else:
195+
cleaned_messages.append(msg)
196+
return cleaned_messages
197+
174198
def find(self, model: str, messages: List[Message]) -> Optional[ConversationInStore]:
175199
"""
176200
Search conversation data by message list.
@@ -185,16 +209,38 @@ def find(self, model: str, messages: List[Message]) -> Optional[ConversationInSt
185209
if not messages:
186210
return None
187211

212+
# --- Find with raw messages ---
213+
if conv := self._find_by_message_list(model, messages):
214+
logger.debug("Found conversation with raw message history.")
215+
return conv
216+
217+
# --- Find with cleaned messages ---
218+
cleaned_messages = self.clean_assistant_messages(messages)
219+
if conv := self._find_by_message_list(model, cleaned_messages):
220+
logger.debug("Found conversation with cleaned message history.")
221+
return conv
222+
223+
logger.debug("No conversation found for either raw or cleaned history.")
224+
return None
225+
226+
def _find_by_message_list(
227+
self, model: str, messages: List[Message]
228+
) -> Optional[ConversationInStore]:
229+
"""Internal find implementation based on a message list."""
188230
for c in g_config.gemini.clients:
189231
message_hash = hash_conversation(c.id, model, messages)
232+
190233
key = f"{self.HASH_LOOKUP_PREFIX}{message_hash}"
191234
try:
192235
with self._get_transaction(write=False) as txn:
193236
mapped = txn.get(key.encode("utf-8"))
194237
if mapped:
238+
logger.debug(f"Found mapped key '{mapped.decode('utf-8')}' for hash '{message_hash}'.")
195239
return self.get(mapped.decode("utf-8")) # type: ignore
196240
except Exception as e:
197-
logger.error(f"Failed to retrieve messages by message list for client {c.id}: {e}")
241+
logger.error(
242+
f"Failed to retrieve messages by message list for hash {message_hash} and client {c.id}: {e}"
243+
)
198244
continue
199245

200246
if conv := self.get(message_hash):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ dependencies = [
1111
"loguru>=0.7.0",
1212
"pydantic-settings[yaml]>=2.9.1",
1313
"uvicorn>=0.34.1",
14-
"uvloop>=0.21.0",
14+
"uvloop>=0.21.0; sys_platform != 'win32'",
1515
]
1616

1717
[project.optional-dependencies]

0 commit comments

Comments
 (0)