GitHub Actions commited on
Commit
fdfd35a
·
1 Parent(s): ff861b8

Sync from GitHub Actions

Browse files
Lawverse/agents/tools.py CHANGED
@@ -7,13 +7,13 @@ from langchain_core.documents import Document
7
  def retrieve_with_hybrid_tool(retriever, query: str, top_k: int = 5) -> List[Document]:
8
  if retriever is None:
9
  return []
10
- try:
 
11
  docs = retriever.invoke(query)
12
- except Exception:
13
- try:
14
- docs = retriever.get_relevant_documents(query)
15
- except Exception:
16
- docs = retriever._get_relevant_documents(query)
17
 
18
  return list(docs or [])[:top_k]
19
 
 
7
  def retrieve_with_hybrid_tool(retriever, query: str, top_k: int = 5) -> List[Document]:
8
  if retriever is None:
9
  return []
10
+
11
+ if hasattr(retriever, "invoke"):
12
  docs = retriever.invoke(query)
13
+ elif hasattr(retriever, "get_relevant_documents"):
14
+ docs = retriever.get_relevant_documents(query)
15
+ else:
16
+ docs = retriever._get_relevant_documents(query)
 
17
 
18
  return list(docs or [])[:top_k]
19
 
Lawverse/memory/langchain_memory.py CHANGED
@@ -1,28 +1,42 @@
1
  import sys
2
  from flask import session, has_request_context
3
  from datetime import datetime
4
- from langchain_classic.memory import ConversationBufferMemory
5
  from Lawverse.logger import logging
6
  from Lawverse.exception import ExceptionHandle
7
  from Lawverse.storage.factory import get_chat_store
8
 
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  class ChatMemory:
11
  def __init__(self, chat_id=None, user_id=None):
12
  try:
13
  self.chat_id = chat_id or self._create_new_chat_id()
14
  self.user_id = str(user_id or self._get_current_user_id())
15
  self.store = get_chat_store()
16
-
17
- self.memory = ConversationBufferMemory(
18
- memory_key="chat_history",
19
- return_messages=True,
20
- output_key="answer",
21
- )
22
-
23
  self._load_memory()
24
  logging.info(f"ChatMemory initialized for user_id={self.user_id}, chat_id={self.chat_id}")
25
-
26
  except Exception as e:
27
  raise ExceptionHandle(e, sys) from e
28
 
@@ -37,22 +51,19 @@ class ChatMemory:
37
  def _load_memory(self):
38
  try:
39
  data = self.store.load_chat(self.user_id, self.chat_id)
40
-
41
  if not data:
42
  logging.info(f"No existing memory found for chat_id: {self.chat_id}")
43
  return
44
 
45
- for msg in data.get("history", []):
46
  user_msg = msg.get("user", "")
47
  ai_msg = msg.get("ai", "")
48
-
49
  if user_msg:
50
  self.memory.chat_memory.add_user_message(user_msg)
51
  if ai_msg:
52
  self.memory.chat_memory.add_ai_message(ai_msg)
53
 
54
  logging.info(f"Memory loaded successfully for chat_id: {self.chat_id}")
55
-
56
  except Exception as e:
57
  raise ExceptionHandle(e, sys) from e
58
 
@@ -65,19 +76,16 @@ class ChatMemory:
65
  def _history_as_pairs(self):
66
  messages = self.memory.chat_memory.messages
67
  history = []
68
-
69
  i = 0
70
  while i < len(messages):
71
  user_msg = messages[i].content if i < len(messages) else ""
72
  ai_msg = messages[i + 1].content if i + 1 < len(messages) else ""
73
-
74
  if user_msg or ai_msg:
75
  history.append({"user": user_msg, "ai": ai_msg})
76
  i += 2
77
-
78
  return history
79
 
80
- def save_memory(self):
81
  try:
82
  self.store.save_chat(
83
  user_id=self.user_id,
@@ -85,14 +93,17 @@ class ChatMemory:
85
  title=self._get_title(),
86
  history=self._history_as_pairs(),
87
  )
88
-
89
  logging.info(f"Memory saved successfully for chat_id: {self.chat_id}")
 
90
  except Exception as e:
91
- raise ExceptionHandle(e, sys) from e
 
92
 
93
  def _get_title(self):
94
- if self.memory.chat_memory.messages:
95
- return self.memory.chat_memory.messages[0].content[:40]
 
 
96
  return "New legal query"
97
 
98
  def clear_memory(self):
@@ -100,6 +111,5 @@ class ChatMemory:
100
  self.memory.clear()
101
  self.store.delete_chat(self.user_id, self.chat_id)
102
  logging.info(f"Memory deleted for chat_id: {self.chat_id}")
103
-
104
  except Exception as e:
105
  raise ExceptionHandle(e, sys) from e
 
1
  import sys
2
  from flask import session, has_request_context
3
  from datetime import datetime
4
+ from langchain_core.messages import HumanMessage, AIMessage
5
  from Lawverse.logger import logging
6
  from Lawverse.exception import ExceptionHandle
7
  from Lawverse.storage.factory import get_chat_store
8
 
9
 
10
+ class _SimpleChatHistory:
11
+ def __init__(self):
12
+ self.messages = []
13
+
14
+ def add_user_message(self, content: str):
15
+ self.messages.append(HumanMessage(content=content or ""))
16
+
17
+ def add_ai_message(self, content: str):
18
+ self.messages.append(AIMessage(content=content or ""))
19
+
20
+ def clear(self):
21
+ self.messages.clear()
22
+
23
+
24
+ class _SimpleMemory:
25
+ def __init__(self):
26
+ self.chat_memory = _SimpleChatHistory()
27
+
28
+ def clear(self):
29
+ self.chat_memory.clear()
30
+
31
  class ChatMemory:
32
  def __init__(self, chat_id=None, user_id=None):
33
  try:
34
  self.chat_id = chat_id or self._create_new_chat_id()
35
  self.user_id = str(user_id or self._get_current_user_id())
36
  self.store = get_chat_store()
37
+ self.memory = _SimpleMemory()
 
 
 
 
 
 
38
  self._load_memory()
39
  logging.info(f"ChatMemory initialized for user_id={self.user_id}, chat_id={self.chat_id}")
 
40
  except Exception as e:
41
  raise ExceptionHandle(e, sys) from e
42
 
 
51
  def _load_memory(self):
52
  try:
53
  data = self.store.load_chat(self.user_id, self.chat_id)
 
54
  if not data:
55
  logging.info(f"No existing memory found for chat_id: {self.chat_id}")
56
  return
57
 
58
+ for msg in data.get("history", []) or []:
59
  user_msg = msg.get("user", "")
60
  ai_msg = msg.get("ai", "")
 
61
  if user_msg:
62
  self.memory.chat_memory.add_user_message(user_msg)
63
  if ai_msg:
64
  self.memory.chat_memory.add_ai_message(ai_msg)
65
 
66
  logging.info(f"Memory loaded successfully for chat_id: {self.chat_id}")
 
67
  except Exception as e:
68
  raise ExceptionHandle(e, sys) from e
69
 
 
76
  def _history_as_pairs(self):
77
  messages = self.memory.chat_memory.messages
78
  history = []
 
79
  i = 0
80
  while i < len(messages):
81
  user_msg = messages[i].content if i < len(messages) else ""
82
  ai_msg = messages[i + 1].content if i + 1 < len(messages) else ""
 
83
  if user_msg or ai_msg:
84
  history.append({"user": user_msg, "ai": ai_msg})
85
  i += 2
 
86
  return history
87
 
88
+ def save_memory(self) -> bool:
89
  try:
90
  self.store.save_chat(
91
  user_id=self.user_id,
 
93
  title=self._get_title(),
94
  history=self._history_as_pairs(),
95
  )
 
96
  logging.info(f"Memory saved successfully for chat_id: {self.chat_id}")
97
+ return True
98
  except Exception as e:
99
+ logging.error(f"Memory save failed for chat_id={self.chat_id}: {e}")
100
+ return False
101
 
102
  def _get_title(self):
103
+ for msg in self.memory.chat_memory.messages:
104
+ content = getattr(msg, "content", "") or ""
105
+ if content.strip():
106
+ return content.strip()[:40]
107
  return "New legal query"
108
 
109
  def clear_memory(self):
 
111
  self.memory.clear()
112
  self.store.delete_chat(self.user_id, self.chat_id)
113
  logging.info(f"Memory deleted for chat_id: {self.chat_id}")
 
114
  except Exception as e:
115
  raise ExceptionHandle(e, sys) from e
Lawverse/retrieval/hybrid.py CHANGED
@@ -37,7 +37,7 @@ def hybrid_retrieve(
37
  ):
38
  try:
39
  dense_results = faiss_db.similarity_search(query, k=initial_top_k)
40
- sparse_results = bm25_retrieve(bm25, chunks, query, top_k=initial_top_k)
41
 
42
  doc_map: Dict[str, Document] = {}
43
  rrf_scores = defaultdict(float)
 
37
  ):
38
  try:
39
  dense_results = faiss_db.similarity_search(query, k=initial_top_k)
40
+ sparse_results = bm25_retrieve(bm25, query, chunks, top_k=initial_top_k)
41
 
42
  doc_map: Dict[str, Document] = {}
43
  rrf_scores = defaultdict(float)
Lawverse/retrieval/sparse.py CHANGED
@@ -1,7 +1,7 @@
1
  from __future__ import annotations
2
  import re
3
  import sys
4
- from typing import List, Tuple, Union
5
  from rank_bm25 import BM25Okapi
6
  from langchain_core.documents import Document
7
  from Lawverse.logger import logging
@@ -18,41 +18,34 @@ STOP_WORDS = {
18
  }
19
 
20
 
21
- def bm25_tokenizer(text: Union[str, List[str], tuple]) -> List[str]:
22
  if text is None:
23
  return []
24
 
25
- if isinstance(text, (list, tuple)):
26
- return [
27
- str(token).lower().strip()
28
- for token in text
29
- if str(token).strip() and str(token).lower().strip() not in STOP_WORDS
30
- ]
31
 
32
  text = str(text).lower()
33
  english_tokens = re.findall(r"[a-zA-Z0-9]+", text)
34
  bangla_tokens = re.findall(r"[\u0980-\u09FF]+", text)
35
  tokens = english_tokens + bangla_tokens
36
-
37
- return [
38
- token
39
- for token in tokens
40
- if len(token) > 1 and token not in STOP_WORDS
41
- ]
42
 
43
 
44
  def build_sparse_index(chunks: List[Document], k1: float = 1.5, b: float = 0.8) -> BM25Okapi:
45
  try:
46
  logging.info("Building sparse BM25 index...")
47
- tokenized_corpus = [
48
- bm25_tokenizer(chunk.page_content)
49
- for chunk in chunks
50
- ]
51
-
 
 
52
  bm25 = BM25Okapi(tokenized_corpus, k1=k1, b=b)
53
- logging.info(f"BM25 sparse index successfully built with {len(chunks)} chunks.")
54
  return bm25
55
-
56
  except Exception as e:
57
  logging.error(f"Failed to build BM25 sparse index. Error: {e}")
58
  raise ExceptionHandle(e, sys)
@@ -65,24 +58,28 @@ def bm25_retrieve(
65
  top_k: int = 10,
66
  ) -> List[Tuple[Document, float]]:
67
  try:
 
 
 
 
 
68
  query_tokens = bm25_tokenizer(query)
69
  scores = bm25.get_scores(query_tokens)
 
70
 
71
- ranked = sorted(
72
- enumerate(scores),
73
- key=lambda item: item[1],
74
- reverse=True,
75
- )[:top_k]
76
-
77
- results = []
78
  for idx, score in ranked:
 
 
79
  doc = chunks[idx]
80
- doc.metadata = dict(doc.metadata or {})
 
 
 
81
  doc.metadata["bm25_score"] = float(score)
82
  results.append((doc, float(score)))
83
 
84
  return results
85
-
86
  except Exception as e:
87
  logging.error(f"BM25 retrieval failed. Error: {e}")
88
  raise ExceptionHandle(e, sys)
 
1
  from __future__ import annotations
2
  import re
3
  import sys
4
+ from typing import List, Tuple, Union, Iterable
5
  from rank_bm25 import BM25Okapi
6
  from langchain_core.documents import Document
7
  from Lawverse.logger import logging
 
18
  }
19
 
20
 
21
+ def bm25_tokenizer(text: Union[str, Iterable[str], None]) -> List[str]:
22
  if text is None:
23
  return []
24
 
25
+ if isinstance(text, (list, tuple, set)):
26
+ tokens = [str(token).lower().strip() for token in text]
27
+ return [token for token in tokens if len(token) > 1 and token not in STOP_WORDS]
 
 
 
28
 
29
  text = str(text).lower()
30
  english_tokens = re.findall(r"[a-zA-Z0-9]+", text)
31
  bangla_tokens = re.findall(r"[\u0980-\u09FF]+", text)
32
  tokens = english_tokens + bangla_tokens
33
+ return [token for token in tokens if len(token) > 1 and token not in STOP_WORDS]
 
 
 
 
 
34
 
35
 
36
  def build_sparse_index(chunks: List[Document], k1: float = 1.5, b: float = 0.8) -> BM25Okapi:
37
  try:
38
  logging.info("Building sparse BM25 index...")
39
+ safe_chunks = [chunk for chunk in chunks if isinstance(chunk, Document)]
40
+ if len(safe_chunks) != len(chunks):
41
+ logging.warning(
42
+ "BM25 received non-Document chunks; ignored %s invalid items.",
43
+ len(chunks) - len(safe_chunks),
44
+ )
45
+ tokenized_corpus = [bm25_tokenizer(chunk.page_content) for chunk in safe_chunks]
46
  bm25 = BM25Okapi(tokenized_corpus, k1=k1, b=b)
47
+ logging.info(f"BM25 sparse index successfully built with {len(safe_chunks)} chunks.")
48
  return bm25
 
49
  except Exception as e:
50
  logging.error(f"Failed to build BM25 sparse index. Error: {e}")
51
  raise ExceptionHandle(e, sys)
 
58
  top_k: int = 10,
59
  ) -> List[Tuple[Document, float]]:
60
  try:
61
+ if not isinstance(chunks, list):
62
+ raise TypeError(
63
+ f"chunks must be a list[Document], got {type(chunks).__name__}. "
64
+ )
65
+
66
  query_tokens = bm25_tokenizer(query)
67
  scores = bm25.get_scores(query_tokens)
68
+ ranked = sorted(enumerate(scores), key=lambda item: item[1], reverse=True)[:top_k]
69
 
70
+ results: List[Tuple[Document, float]] = []
 
 
 
 
 
 
71
  for idx, score in ranked:
72
+ if idx >= len(chunks):
73
+ continue
74
  doc = chunks[idx]
75
+ if not isinstance(doc, Document):
76
+ logging.warning("Skipping BM25 result with invalid chunk type: %s", type(doc).__name__)
77
+ continue
78
+ doc = Document(page_content=doc.page_content, metadata=dict(doc.metadata or {}))
79
  doc.metadata["bm25_score"] = float(score)
80
  results.append((doc, float(score)))
81
 
82
  return results
 
83
  except Exception as e:
84
  logging.error(f"BM25 retrieval failed. Error: {e}")
85
  raise ExceptionHandle(e, sys)
api/app.py CHANGED
@@ -1,8 +1,8 @@
1
  from flask import Flask, render_template, request, jsonify, session, stream_with_context, Response
2
  from dotenv import load_dotenv
3
- import secrets
4
- import os
5
  from threading import Lock
 
 
6
  import logging as py_logging
7
  from Lawverse.pipeline.rag_pipeline import rag_components
8
  from Lawverse.pipeline.llm_loader import llm
@@ -13,15 +13,12 @@ from Lawverse.agents.graph import create_agentic_chain
13
  from Lawverse.storage.factory import get_chat_store
14
  from api.auth import auth_bp, login_required
15
  for logger_name in [
16
- "httpcore",
17
- "httpx",
18
- "hpack",
19
- "filelock",
20
- "sentence_transformers",
21
- "urllib3",
22
  ]:
23
  py_logging.getLogger(logger_name).setLevel(py_logging.WARNING)
24
-
 
25
  load_dotenv()
26
  app = Flask(__name__, template_folder="../templates")
27
  app.secret_key = os.getenv("SECRET_KEY") or secrets.token_hex(32)
@@ -31,11 +28,12 @@ app.register_blueprint(monitor_bp)
31
 
32
  BASE_COMPONENTS = None
33
  BASE_COMPONENTS_LOCK = Lock()
 
 
34
  active_chains = {}
35
 
36
  def get_base_components():
37
  global BASE_COMPONENTS
38
-
39
  if BASE_COMPONENTS is not None:
40
  return BASE_COMPONENTS
41
 
@@ -44,19 +42,28 @@ def get_base_components():
44
  logging.info("Loading Lawverse RAG base components...")
45
  BASE_COMPONENTS = rag_components()
46
  logging.info("Lawverse RAG base components loaded successfully.")
47
-
48
  return BASE_COMPONENTS
49
 
50
 
51
- def create_agent_session(chat_id=None):
52
- components = get_base_components()
53
- chain = create_agentic_chain(components, llm)
54
- memory_manager = ChatMemory(chat_id=chat_id)
55
 
 
 
 
 
 
 
 
 
 
 
56
  active_chains[memory_manager.chat_id] = (chain, memory_manager)
57
  session["chat_id"] = memory_manager.chat_id
58
- memory_manager.save_memory()
59
-
60
  return chain, memory_manager
61
 
62
  @app.route("/", methods=["GET"])
@@ -69,18 +76,13 @@ def chat():
69
  chat_id = session.get("chat_id")
70
  if not chat_id or chat_id not in active_chains:
71
  create_agent_session()
72
-
73
  return render_template("chat.html")
74
 
75
  @app.route("/new_chat", methods=["POST"])
76
  @login_required
77
  def new_chat():
78
  _, memory_manager = create_agent_session()
79
-
80
- return jsonify({
81
- "chat_id": memory_manager.chat_id,
82
- "title": memory_manager._get_title()
83
- })
84
 
85
 
86
  @app.route("/response", methods=["POST"])
@@ -95,12 +97,12 @@ def rag_response():
95
  qa, memory_manager = active_chains[chat_id]
96
  data = request.get_json(silent=True) or {}
97
  query = data.get("message", "").strip()
98
-
99
  if not query:
100
  return jsonify({"error": "Empty message"}), 400
101
 
102
  def generate():
103
  answer_parts = []
 
104
  try:
105
  for chunk in qa.stream({
106
  "input": query,
@@ -109,15 +111,15 @@ def rag_response():
109
  text = chunk if isinstance(chunk, str) else str(chunk)
110
  answer_parts.append(text)
111
  yield text
 
 
 
112
 
 
113
  full_answer = "".join(answer_parts).strip()
114
  memory_manager.append_exchange(query, full_answer)
115
  memory_manager.save_memory()
116
 
117
- except Exception as e:
118
- logging.error(f"Error during stream generation: {e}")
119
- yield "**Error:** An error occurred while processing your request."
120
-
121
  return Response(stream_with_context(generate()), mimetype="text/plain")
122
 
123
  except Exception as e:
@@ -132,7 +134,6 @@ def get_chats():
132
  user_id = str(session.get("user_id"))
133
  chats = get_chat_store().list_chats(user_id)
134
  return jsonify(chats), 200
135
-
136
  except Exception as e:
137
  logging.error(f"Failed to list chats from cloud store: {e}")
138
  return jsonify([]), 200
@@ -147,27 +148,20 @@ def load_chat(chat_id):
147
  if not data:
148
  return jsonify({"error": "Chat not found"}), 404
149
 
150
- _, memory_manager = create_agent_session(chat_id=chat_id)
151
-
152
  messages_list = memory_manager.memory.chat_memory.messages
153
  messages = []
154
-
155
  for i in range(0, len(messages_list), 2):
156
  user_msg = messages_list[i].content if i < len(messages_list) else None
157
  ai_msg = messages_list[i + 1].content if i + 1 < len(messages_list) else ""
158
-
159
  if user_msg:
160
- messages.append({
161
- "user": user_msg,
162
- "ai": ai_msg
163
- })
164
 
165
  return jsonify({
166
  "chat_id": chat_id,
167
  "title": memory_manager._get_title(),
168
  "messages": messages,
169
  }), 200
170
-
171
  except Exception as e:
172
  logging.error(f"Failed to load chat from cloud store: {e}")
173
  return jsonify({"error": "Internal Server Error"}), 500
@@ -178,21 +172,13 @@ def load_chat(chat_id):
178
  def delete_chat(chat_id):
179
  try:
180
  user_id = str(session.get("user_id"))
181
-
182
  deleted = get_chat_store().delete_chat(user_id, chat_id)
183
-
184
  was_active = chat_id in active_chains
185
  if was_active:
186
  del active_chains[chat_id]
187
-
188
  if session.get("chat_id") == chat_id:
189
  session.pop("chat_id", None)
190
-
191
- return jsonify({
192
- "success": deleted,
193
- "was_active": was_active
194
- }), 200
195
-
196
  except Exception as e:
197
  logging.error(f"Error deleting cloud chat {chat_id}: {e}")
198
  return jsonify({"error": "Internal Server Error"}), 500
 
1
  from flask import Flask, render_template, request, jsonify, session, stream_with_context, Response
2
  from dotenv import load_dotenv
 
 
3
  from threading import Lock
4
+ import os
5
+ import secrets
6
  import logging as py_logging
7
  from Lawverse.pipeline.rag_pipeline import rag_components
8
  from Lawverse.pipeline.llm_loader import llm
 
13
  from Lawverse.storage.factory import get_chat_store
14
  from api.auth import auth_bp, login_required
15
  for logger_name in [
16
+ "httpcore", "httpx", "hpack", "filelock", "sentence_transformers",
17
+ "urllib3", "openai", "openai._base_client", "faiss", "datasets",
 
 
 
 
18
  ]:
19
  py_logging.getLogger(logger_name).setLevel(py_logging.WARNING)
20
+
21
+
22
  load_dotenv()
23
  app = Flask(__name__, template_folder="../templates")
24
  app.secret_key = os.getenv("SECRET_KEY") or secrets.token_hex(32)
 
28
 
29
  BASE_COMPONENTS = None
30
  BASE_COMPONENTS_LOCK = Lock()
31
+ AGENT_CHAIN = None
32
+ AGENT_CHAIN_LOCK = Lock()
33
  active_chains = {}
34
 
35
  def get_base_components():
36
  global BASE_COMPONENTS
 
37
  if BASE_COMPONENTS is not None:
38
  return BASE_COMPONENTS
39
 
 
42
  logging.info("Loading Lawverse RAG base components...")
43
  BASE_COMPONENTS = rag_components()
44
  logging.info("Lawverse RAG base components loaded successfully.")
 
45
  return BASE_COMPONENTS
46
 
47
 
48
+ def get_agent_chain():
49
+ global AGENT_CHAIN
50
+ if AGENT_CHAIN is not None:
51
+ return AGENT_CHAIN
52
 
53
+ with AGENT_CHAIN_LOCK:
54
+ if AGENT_CHAIN is None:
55
+ components = get_base_components()
56
+ AGENT_CHAIN = create_agentic_chain(components, llm)
57
+ return AGENT_CHAIN
58
+
59
+
60
+ def create_agent_session(chat_id=None, save_on_create=True):
61
+ chain = get_agent_chain()
62
+ memory_manager = ChatMemory(chat_id=chat_id)
63
  active_chains[memory_manager.chat_id] = (chain, memory_manager)
64
  session["chat_id"] = memory_manager.chat_id
65
+ if save_on_create:
66
+ memory_manager.save_memory()
67
  return chain, memory_manager
68
 
69
  @app.route("/", methods=["GET"])
 
76
  chat_id = session.get("chat_id")
77
  if not chat_id or chat_id not in active_chains:
78
  create_agent_session()
 
79
  return render_template("chat.html")
80
 
81
  @app.route("/new_chat", methods=["POST"])
82
  @login_required
83
  def new_chat():
84
  _, memory_manager = create_agent_session()
85
+ return jsonify({"chat_id": memory_manager.chat_id, "title": memory_manager._get_title()})
 
 
 
 
86
 
87
 
88
  @app.route("/response", methods=["POST"])
 
97
  qa, memory_manager = active_chains[chat_id]
98
  data = request.get_json(silent=True) or {}
99
  query = data.get("message", "").strip()
 
100
  if not query:
101
  return jsonify({"error": "Empty message"}), 400
102
 
103
  def generate():
104
  answer_parts = []
105
+ generation_failed = False
106
  try:
107
  for chunk in qa.stream({
108
  "input": query,
 
111
  text = chunk if isinstance(chunk, str) else str(chunk)
112
  answer_parts.append(text)
113
  yield text
114
+ except Exception as e:
115
+ generation_failed = True
116
+ logging.error(f"Error during answer generation: {e}")
117
 
118
+ if not generation_failed:
119
  full_answer = "".join(answer_parts).strip()
120
  memory_manager.append_exchange(query, full_answer)
121
  memory_manager.save_memory()
122
 
 
 
 
 
123
  return Response(stream_with_context(generate()), mimetype="text/plain")
124
 
125
  except Exception as e:
 
134
  user_id = str(session.get("user_id"))
135
  chats = get_chat_store().list_chats(user_id)
136
  return jsonify(chats), 200
 
137
  except Exception as e:
138
  logging.error(f"Failed to list chats from cloud store: {e}")
139
  return jsonify([]), 200
 
148
  if not data:
149
  return jsonify({"error": "Chat not found"}), 404
150
 
151
+ _, memory_manager = create_agent_session(chat_id=chat_id, save_on_create=False)
 
152
  messages_list = memory_manager.memory.chat_memory.messages
153
  messages = []
 
154
  for i in range(0, len(messages_list), 2):
155
  user_msg = messages_list[i].content if i < len(messages_list) else None
156
  ai_msg = messages_list[i + 1].content if i + 1 < len(messages_list) else ""
 
157
  if user_msg:
158
+ messages.append({"user": user_msg, "ai": ai_msg})
 
 
 
159
 
160
  return jsonify({
161
  "chat_id": chat_id,
162
  "title": memory_manager._get_title(),
163
  "messages": messages,
164
  }), 200
 
165
  except Exception as e:
166
  logging.error(f"Failed to load chat from cloud store: {e}")
167
  return jsonify({"error": "Internal Server Error"}), 500
 
172
  def delete_chat(chat_id):
173
  try:
174
  user_id = str(session.get("user_id"))
 
175
  deleted = get_chat_store().delete_chat(user_id, chat_id)
 
176
  was_active = chat_id in active_chains
177
  if was_active:
178
  del active_chains[chat_id]
 
179
  if session.get("chat_id") == chat_id:
180
  session.pop("chat_id", None)
181
+ return jsonify({"success": deleted, "was_active": was_active}), 200
 
 
 
 
 
182
  except Exception as e:
183
  logging.error(f"Error deleting cloud chat {chat_id}: {e}")
184
  return jsonify({"error": "Internal Server Error"}), 500