AhmadYarAI commited on
Commit
16a0a0a
Β·
1 Parent(s): 31f12a4

Chat Messages send and receive and also load the history

Browse files
Files changed (3) hide show
  1. app/db/models.py +24 -36
  2. app/db/models_chat.py +30 -0
  3. app/routers/chat_ws.py +63 -12
app/db/models.py CHANGED
@@ -20,13 +20,33 @@ class UserDB(Base):
20
  password = Column(String(255), nullable=False)
21
 
22
  role = Column(String(20), nullable=False) # "head" or "member"
23
- family_code = Column(String(10), nullable=True) # Only for members
 
24
  otp_code = Column(String(6), nullable=True)
25
  otp_expiry = Column(DateTime, nullable=True)
26
- created_at = Column(DateTime(timezone=True), server_default=func.now())
27
- family = relationship("Family", back_populates="head", uselist=False)
28
- usage = relationship("UserUsage", back_populates="user", uselist=False)
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  class UserUsage(Base):
31
  __tablename__ = "user_usage"
32
  id = Column(Integer, primary_key=True, index=True)
@@ -41,35 +61,3 @@ class UserUsage(Base):
41
  user = relationship("UserDB", back_populates="usage")
42
 
43
 
44
- # class PostDB(Base):
45
- # __tablename__ = "posts"
46
- # id = Column(Integer, primary_key=True, index=True)
47
- # user_id = Column(Integer, ForeignKey("user_accounts.id", ondelete="CASCADE"), nullable=False)
48
- # content = Column(Text, nullable=True)
49
- # image_url = Column(String(300), nullable=True)
50
- # created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
51
-
52
- # user = relationship("UserDB")
53
- # comments = relationship("CommentDB", backref="post", cascade="all, delete-orphan")
54
- # likes = relationship("LikeDB", back_populates="post", cascade="all, delete-orphan")
55
-
56
- # class CommentDB(Base):
57
- # __tablename__ = "comments"
58
- # id = Column(Integer, primary_key=True, index=True)
59
- # content = Column(Text, nullable=False)
60
- # created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
61
- # post_id = Column(Integer, ForeignKey("posts.id", ondelete="CASCADE"), nullable=False)
62
- # user_id = Column(Integer, ForeignKey("user_accounts.id", ondelete="CASCADE"), nullable=False)
63
- # user = relationship("UserDB")
64
-
65
- # class LikeDB(Base):
66
- # __tablename__ = "likes"
67
- # id = Column(Integer, primary_key=True, index=True)
68
- # user_id = Column(Integer, ForeignKey("user_accounts.id", ondelete="CASCADE"), nullable=False)
69
- # post_id = Column(Integer, ForeignKey("posts.id", ondelete="CASCADE"), nullable=False)
70
- # created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
71
-
72
- # __table_args__ = (UniqueConstraint("user_id", "post_id", name="unique_like"),)
73
-
74
- # user = relationship("UserDB")
75
- # post = relationship("PostDB", back_populates="likes")
 
20
  password = Column(String(255), nullable=False)
21
 
22
  role = Column(String(20), nullable=False) # "head" or "member"
23
+ family_code = Column(String(10), nullable=True)
24
+
25
  otp_code = Column(String(6), nullable=True)
26
  otp_expiry = Column(DateTime, nullable=True)
 
 
 
27
 
28
+ created_at = Column(
29
+ DateTime(timezone=True),
30
+ server_default=func.now()
31
+ )
32
+
33
+ family = relationship(
34
+ "Family",
35
+ back_populates="head",
36
+ uselist=False
37
+ )
38
+
39
+ usage = relationship(
40
+ "UserUsage",
41
+ back_populates="user",
42
+ uselist=False
43
+ )
44
+
45
+ chats = relationship(
46
+ "ChatMessage",
47
+ back_populates="user",
48
+ cascade="all, delete-orphan"
49
+ )
50
  class UserUsage(Base):
51
  __tablename__ = "user_usage"
52
  id = Column(Integer, primary_key=True, index=True)
 
61
  user = relationship("UserDB", back_populates="usage")
62
 
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/db/models_chat.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
2
+ from sqlalchemy.orm import relationship
3
+ from sqlalchemy.sql import func
4
+
5
+ from app.db.database import Base
6
+
7
+
8
+ class ChatMessage(Base):
9
+ __tablename__ = "chat_messages"
10
+
11
+ id = Column(Integer, primary_key=True, index=True)
12
+
13
+ user_id = Column(
14
+ Integer,
15
+ ForeignKey("user_accounts.id", ondelete="CASCADE"),
16
+ nullable=False
17
+ )
18
+
19
+ # user OR assistant
20
+ role = Column(String(20), nullable=False)
21
+
22
+ message = Column(Text, nullable=False)
23
+
24
+ created_at = Column(
25
+ DateTime(timezone=True),
26
+ server_default=func.now()
27
+ )
28
+
29
+ # relationship
30
+ user = relationship("UserDB", back_populates="chats")
app/routers/chat_ws.py CHANGED
@@ -11,7 +11,7 @@ from app.services.date_extractor import DateExtractor
11
  from datetime import datetime
12
  import calendar
13
  from app.db.models import UserUsage
14
-
15
  router = APIRouter()
16
  ai = OpenAIService()
17
  date_extractor = DateExtractor()
@@ -298,8 +298,10 @@ async def chat_socket(websocket: WebSocket):
298
  continue
299
 
300
  user_message = str(data["content"]).strip()
301
- usage = get_or_create_usage(db, user_id)
302
 
 
 
303
  print(f"[Chat] user_id={user_id} | count={usage.request_count} | paid={usage.is_paid}")
304
 
305
  # ── Usage gate ──
@@ -319,26 +321,31 @@ async def chat_socket(websocket: WebSocket):
319
 
320
  # ── Greeting β€” no usage count ──
321
  if intent == "greeting":
 
 
 
 
 
 
 
 
 
 
322
  await websocket.send_json({
323
  "type": "assistant_message",
324
  "allowed": True,
325
- "content": (
326
- "Hello! I'm FinanceAI, your personal finance assistant πŸ’°\n\n"
327
- "I can help you with:\n"
328
- "β€’ Your monthly budget breakdown\n"
329
- "β€’ Spending predictions for next month\n"
330
- "β€’ Tips to control and reduce your expenses\n\n"
331
- "What would you like to know?"
332
- ),
333
  "used_requests": usage.request_count,
334
  "remaining_requests": max(MAX_FREE_REQUESTS - usage.request_count, 0),
335
  "is_paid": usage.is_paid
336
  })
337
- continue
338
 
 
339
  # ── General non-finance chat ──
340
  if intent == "general":
341
  ai_reply = ai.chat_with_context(user_message=user_message, finance_data=None)
 
 
342
  await send_and_count(websocket, db, usage, ai_reply)
343
  continue
344
 
@@ -395,6 +402,8 @@ async def chat_socket(websocket: WebSocket):
395
  }
396
 
397
  ai_reply = ai.chat_with_context(user_message=user_message, finance_data=finance_context)
 
 
398
  await send_and_count(websocket, db, usage, ai_reply)
399
  continue
400
 
@@ -429,6 +438,8 @@ async def chat_socket(websocket: WebSocket):
429
  }
430
 
431
  ai_reply = ai.chat_with_context(user_message=user_message, finance_data=finance_context)
 
 
432
  await send_and_count(websocket, db, usage, ai_reply)
433
  continue
434
 
@@ -471,10 +482,50 @@ async def chat_socket(websocket: WebSocket):
471
  }
472
 
473
  ai_reply = ai.chat_with_context(user_message=user_message, finance_data=finance_context)
 
 
474
  await send_and_count(websocket, db, usage, ai_reply)
475
 
476
  except WebSocketDisconnect:
477
  pass
478
 
479
  finally:
480
- db.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  from datetime import datetime
12
  import calendar
13
  from app.db.models import UserUsage
14
+ from app.db.models_chat import ChatMessage
15
  router = APIRouter()
16
  ai = OpenAIService()
17
  date_extractor = DateExtractor()
 
298
  continue
299
 
300
  user_message = str(data["content"]).strip()
301
+ # Save user message
302
 
303
+ save_chat(db, user_id, "user", user_message)
304
+ usage = get_or_create_usage(db, user_id)
305
  print(f"[Chat] user_id={user_id} | count={usage.request_count} | paid={usage.is_paid}")
306
 
307
  # ── Usage gate ──
 
321
 
322
  # ── Greeting β€” no usage count ──
323
  if intent == "greeting":
324
+
325
+ greeting_message = (
326
+ "Hello! I'm FinanceAI, your personal finance assistant πŸ’°\n\n"
327
+ "I can help you with:\n"
328
+ "β€’ Your monthly budget breakdown\n"
329
+ "β€’ Spending predictions for next month\n"
330
+ "β€’ Tips to control and reduce your expenses\n\n"
331
+ "What would you like to know?"
332
+ )
333
+ save_chat(db, user_id, "assistant", greeting_message)
334
  await websocket.send_json({
335
  "type": "assistant_message",
336
  "allowed": True,
337
+ "content": greeting_message,
 
 
 
 
 
 
 
338
  "used_requests": usage.request_count,
339
  "remaining_requests": max(MAX_FREE_REQUESTS - usage.request_count, 0),
340
  "is_paid": usage.is_paid
341
  })
 
342
 
343
+ continue
344
  # ── General non-finance chat ──
345
  if intent == "general":
346
  ai_reply = ai.chat_with_context(user_message=user_message, finance_data=None)
347
+ save_chat(db, user_id, "assistant", ai_reply)
348
+
349
  await send_and_count(websocket, db, usage, ai_reply)
350
  continue
351
 
 
402
  }
403
 
404
  ai_reply = ai.chat_with_context(user_message=user_message, finance_data=finance_context)
405
+ save_chat(db, user_id, "assistant", ai_reply)
406
+
407
  await send_and_count(websocket, db, usage, ai_reply)
408
  continue
409
 
 
438
  }
439
 
440
  ai_reply = ai.chat_with_context(user_message=user_message, finance_data=finance_context)
441
+ save_chat(db, user_id, "assistant", ai_reply)
442
+
443
  await send_and_count(websocket, db, usage, ai_reply)
444
  continue
445
 
 
482
  }
483
 
484
  ai_reply = ai.chat_with_context(user_message=user_message, finance_data=finance_context)
485
+ save_chat(db, user_id, "assistant", ai_reply)
486
+
487
  await send_and_count(websocket, db, usage, ai_reply)
488
 
489
  except WebSocketDisconnect:
490
  pass
491
 
492
  finally:
493
+ db.close()
494
+
495
+
496
+ def save_chat(db: Session, user_id: int, role: str, message: str):
497
+ chat = ChatMessage(
498
+ user_id=user_id,
499
+ role=role,
500
+ message=message
501
+ )
502
+
503
+ db.add(chat)
504
+ db.commit()
505
+
506
+ @router.get("/history")
507
+ def get_chat_history(
508
+ current_user: UserDB = Depends(get_current_user),
509
+ db: Session = Depends(get_db)
510
+ ):
511
+ chats = (
512
+ db.query(ChatMessage)
513
+ .filter(ChatMessage.user_id == current_user.id)
514
+ .order_by(ChatMessage.created_at.asc())
515
+ .all()
516
+ )
517
+
518
+ response = []
519
+
520
+ for chat in chats:
521
+ response.append({
522
+ "id": chat.id,
523
+ "role": chat.role,
524
+ "message": chat.message,
525
+ "created_at": chat.created_at
526
+ })
527
+
528
+ return {
529
+ "status": True,
530
+ "messages": response
531
+ }