feat(db): add chat and message models for conversation tracking
Browse filesIntroduce SQLModel-based Chat and Message models to store conversation history. The Chat model tracks user conversations while the Message model stores individual messages with metadata including role, content, answer, and tool-related fields.
- .gitattributes +1 -0
- db/models/chat.py +22 -0
- db/models/message.py +33 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
db/models/chat.py
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import uuid
|
2 |
+
from datetime import datetime
|
3 |
+
from sqlmodel import Field, Relationship, SQLModel
|
4 |
+
from typing import TYPE_CHECKING, list
|
5 |
+
|
6 |
+
if TYPE_CHECKING:
|
7 |
+
from .message import Message
|
8 |
+
|
9 |
+
class Chat(SQLModel, table=True):
|
10 |
+
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
|
11 |
+
user_id: uuid.UUID = Field(index=True)
|
12 |
+
title: str = Field(default="New Chat", max_length=100)
|
13 |
+
created_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
14 |
+
updated_at: datetime = Field(
|
15 |
+
default_factory=datetime.utcnow,
|
16 |
+
nullable=False,
|
17 |
+
sa_column_kwargs={"onupdate": datetime.utcnow}
|
18 |
+
)
|
19 |
+
messages: list["Message"] = Relationship(
|
20 |
+
back_populates="chat",
|
21 |
+
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
|
22 |
+
)
|
db/models/message.py
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import uuid
|
2 |
+
from datetime import datetime
|
3 |
+
from typing import Optional, Literal, Any, TYPE_CHECKING
|
4 |
+
from sqlmodel import Field, Relationship, SQLModel, JSON, Column
|
5 |
+
|
6 |
+
if TYPE_CHECKING:
|
7 |
+
from .chat import Chat
|
8 |
+
|
9 |
+
MessageRole = Literal["user", "assistant", "tool"]
|
10 |
+
|
11 |
+
class Message(SQLModel, table=True):
|
12 |
+
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
13 |
+
chat_id: uuid.UUID = Field(foreign_key="chat.id", index=True)
|
14 |
+
role: MessageRole = Field(nullable=False)
|
15 |
+
|
16 |
+
# Stores the RAW, full text content.
|
17 |
+
# e.g., "The answer is... Links: [http://...]"
|
18 |
+
content: Optional[str] = Field(default=None, sa_column_kwargs={"longtext": True})
|
19 |
+
|
20 |
+
# Stores the CLEAN, parsed answer for display on the frontend.
|
21 |
+
# Populated only for the final AI message of a turn.
|
22 |
+
answer: Optional[str] = Field(default=None, sa_column_kwargs={"longtext": True})
|
23 |
+
|
24 |
+
# Stores the parsed source links for the frontend.
|
25 |
+
links: Optional[list[str]] = Field(default=None, sa_column=Column(JSON))
|
26 |
+
|
27 |
+
# Fields for tool-calling content
|
28 |
+
tool_calls: Optional[list[dict[str, Any]]] = Field(default=None, sa_column=Column(JSON))
|
29 |
+
tool_call_id: Optional[str] = Field(default=None, index=True)
|
30 |
+
|
31 |
+
created_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
32 |
+
|
33 |
+
chat: Optional["Chat"] = Relationship(back_populates="messages")
|