Skip to content

Commit 5ec4103

Browse files
add chatlogs (#186)
1 parent b81df3f commit 5ec4103

File tree

16 files changed

+395
-38
lines changed

16 files changed

+395
-38
lines changed

Dockerfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
FROM python:3.12-bullseye
1+
FROM --platform=linux/x86_64 python:3.12.7-slim
22

33
WORKDIR /usr/src/app
44

5-
RUN pip install --upgrade pip
5+
RUN apt-get update && apt-get install -y \
6+
build-essential \
7+
python3-dev \
8+
&& rm -rf /var/lib/apt/lists/*
69

710
COPY requirements.txt .
8-
911
RUN pip install --no-cache-dir -r requirements.txt
1012

1113
COPY . .

app/admin/chatlogs/__init__.py

Whitespace-only changes.

app/admin/chatlogs/routes.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from fastapi import APIRouter
2+
from typing import Optional
3+
from datetime import datetime
4+
import app.admin.chatlogs.store as store
5+
6+
router = APIRouter(prefix="/chatlogs", tags=["chatlogs"])
7+
8+
9+
@router.get("/")
10+
async def list_chatlogs(
11+
page: int = 1,
12+
limit: int = 10,
13+
start_date: Optional[datetime] = None,
14+
end_date: Optional[datetime] = None,
15+
):
16+
"""Get paginated chat conversation history with optional date filtering"""
17+
return await store.list_chatlogs(page, limit, start_date, end_date)
18+
19+
20+
@router.get("/{thread_id}")
21+
async def get_chat_thread(thread_id: str):
22+
"""Get complete conversation history for a specific thread"""
23+
conversation = await store.get_chat_thread(thread_id)
24+
if not conversation:
25+
return {"error": "Conversation not found"}
26+
27+
return conversation

app/admin/chatlogs/schemas.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from pydantic import BaseModel
2+
from typing import Dict, List, Optional
3+
from datetime import datetime
4+
5+
6+
class ChatMessage(BaseModel):
7+
text: str
8+
context: Optional[Dict] = {}
9+
10+
11+
class ChatThreadInfo(BaseModel):
12+
thread_id: str
13+
date: datetime
14+
15+
16+
class BotNessage(BaseModel):
17+
text: str
18+
19+
20+
class ChatLog(BaseModel):
21+
user_message: ChatMessage
22+
bot_message: List[BotNessage]
23+
date: datetime
24+
context: Optional[Dict] = {}
25+
26+
27+
class ChatLogResponse(BaseModel):
28+
total: int
29+
page: int
30+
limit: int
31+
conversations: List[ChatThreadInfo]

app/admin/chatlogs/store.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from typing import List, Optional
2+
from datetime import datetime
3+
from app.database import client
4+
from .schemas import ChatLog, ChatLogResponse, ChatThreadInfo
5+
6+
# Initialize MongoDB collection
7+
collection = client["chatbot"]["state"]
8+
9+
10+
async def list_chatlogs(
11+
page: int = 1,
12+
limit: int = 10,
13+
start_date: Optional[datetime] = None,
14+
end_date: Optional[datetime] = None,
15+
) -> ChatLogResponse:
16+
skip = (page - 1) * limit
17+
18+
# Build query filter
19+
query = {}
20+
if start_date or end_date:
21+
query["date"] = {}
22+
if start_date:
23+
query["date"]["$gte"] = start_date
24+
if end_date:
25+
query["date"]["$lte"] = end_date
26+
27+
# Get total count of unique threads for pagination
28+
pipeline = [
29+
{"$match": query},
30+
{"$group": {"_id": "$thread_id"}},
31+
{"$count": "total"},
32+
]
33+
result = await collection.aggregate(pipeline).to_list(1)
34+
total = result[0]["total"] if result else 0
35+
36+
# Get paginated results grouped by thread_id with latest date
37+
pipeline = [
38+
{"$match": query},
39+
{"$sort": {"date": -1}},
40+
{
41+
"$group": {
42+
"_id": "$thread_id",
43+
"thread_id": {"$first": "$thread_id"},
44+
"date": {"$first": "$date"},
45+
}
46+
},
47+
{"$sort": {"date": -1}},
48+
{"$skip": skip},
49+
{"$limit": limit},
50+
]
51+
52+
conversations = []
53+
async for doc in collection.aggregate(pipeline):
54+
conversations.append(
55+
ChatThreadInfo(thread_id=doc["thread_id"], date=doc["date"])
56+
)
57+
58+
return ChatLogResponse(
59+
total=total, page=page, limit=limit, conversations=conversations
60+
)
61+
62+
63+
async def get_chat_thread(thread_id: str) -> List[ChatLog]:
64+
"""Get complete conversation history for a specific thread"""
65+
66+
cursor = collection.find({"thread_id": thread_id}).sort("date", 1)
67+
messages = await cursor.to_list(length=None)
68+
69+
if not messages:
70+
return None
71+
72+
chat_logs = []
73+
for msg in messages:
74+
chat_logs.append(
75+
ChatLog(
76+
user_message=msg["user_message"],
77+
bot_message=msg["bot_message"],
78+
date=msg["date"],
79+
context=msg.get("context", {}),
80+
)
81+
)
82+
83+
return chat_logs

app/bot/memory/models.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ def update(self, user_message: UserMessage):
7373
self.missing_parameters = []
7474
self.complete = False
7575
self.current_node = None
76-
self.date = None
7776

7877
def get_active_intent_id(self):
7978
if self.intent:

app/bot/nlu/entity_extractors/crf_entity_extractor.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
import logging
33
from typing import Dict, Any, List, Optional
44
from app.bot.nlu.pipeline import NLUComponent
5+
import os
56

6-
MODEL_NAME = "crf__entity_extractor.model"
7+
MODEL_NAME = "crf_entity_extractor.model"
78
logger = logging.getLogger(__name__)
89

910

@@ -119,7 +120,8 @@ def train(self, training_data: List[Dict[str, Any]], model_path: str) -> None:
119120
"feature.possible_transitions": True,
120121
}
121122
)
122-
trainer.train(f"{model_path}/{MODEL_NAME}")
123+
path = os.path.join(model_path, MODEL_NAME)
124+
trainer.train(path)
123125

124126
def load(self, model_path: str) -> bool:
125127
"""
@@ -129,7 +131,8 @@ def load(self, model_path: str) -> bool:
129131
"""
130132
try:
131133
self.tagger = pycrfsuite.Tagger()
132-
self.tagger.open(f"{model_path}/entity_model.model")
134+
path = os.path.join(model_path, MODEL_NAME)
135+
self.tagger.open(path)
133136
return True
134137
except Exception as e:
135138
logger.error(f"Error loading CRF model: {e}")

app/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from app.admin.train.routes import router as train_router
1212
from app.admin.test.routes import router as test_router
1313
from app.admin.integrations.routes import router as integrations_router
14+
from app.admin.chatlogs.routes import router as chatlogs_router
15+
1416

1517
from app.bot.channels.rest.routes import router as rest_router
1618
from app.bot.channels.facebook.routes import router as facebook_router
@@ -54,6 +56,8 @@ async def root():
5456
admin_router.include_router(train_router)
5557
admin_router.include_router(test_router)
5658
admin_router.include_router(integrations_router)
59+
admin_router.include_router(chatlogs_router)
60+
5761

5862
app.include_router(admin_router)
5963

docker-compose.dev.yml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ version: '2'
22
name: ai-chatbot-framework-dev
33
services:
44
mongodb:
5-
container_name: mongodb
65
image: mongo:4.2.20
76
hostname: mongodb
87
volumes:
98
- mongodbdata:/data
109

1110
migrate:
12-
container_name: migrate
1311
build:
1412
context: .
1513
dockerfile: Dockerfile
@@ -21,7 +19,6 @@ services:
2119
- mongodb
2220

2321
app:
24-
container_name: backend
2522
build:
2623
context: .
2724
dockerfile: Dockerfile
@@ -38,11 +35,9 @@ services:
3835
- mongodb
3936

4037
frontend:
41-
container_name: frontend
42-
hostname: frontend
4338
build:
4439
context: ./frontend
45-
dockerfile: ./dev.Dockerfile
40+
dockerfile: dev.Dockerfile
4641
volumes:
4742
- ./frontend/app:/app/app
4843
- ./frontend/public:/app/public

frontend/.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
node_modules/
2+
.next/

0 commit comments

Comments
 (0)