Spaces:
Runtime error
Runtime error
Commit ·
0faaeae
1
Parent(s): 5757067
added latest models
Browse files- app/models.py +16 -28
- app/routes/responses.py +11 -9
- app/services/response_chat_service.py +13 -9
app/models.py
CHANGED
|
@@ -191,7 +191,7 @@ class Form(Base):
|
|
| 191 |
questions: Mapped[list["FormQuestion"]] = relationship(back_populates="form", cascade="all, delete-orphan", order_by="FormQuestion.question_order")
|
| 192 |
conditional_rules: Mapped[list["ConditionalRule"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 193 |
responses: Mapped[list["FormResponse"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 194 |
-
|
| 195 |
versions: Mapped[list["FormVersion"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 196 |
analytics_events: Mapped[list["FormAnalyticsEvent"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 197 |
uploads: Mapped[list["FormUpload"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
|
@@ -351,23 +351,32 @@ class PublicForm(Base):
|
|
| 351 |
user: Mapped[User] = relationship(back_populates="public_forms")
|
| 352 |
|
| 353 |
|
| 354 |
-
class
|
| 355 |
-
"""
|
| 356 |
-
__tablename__ = "
|
| 357 |
|
| 358 |
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
| 359 |
form_id: Mapped[int] = mapped_column(ForeignKey("forms.id"), index=True)
|
| 360 |
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True)
|
| 361 |
|
|
|
|
|
|
|
|
|
|
| 362 |
role: Mapped[str] = mapped_column(String(20)) # "user", "assistant"
|
| 363 |
content: Mapped[str] = mapped_column(Text)
|
| 364 |
-
query_type: Mapped[str | None] = mapped_column(String(50), nullable=True)
|
| 365 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 366 |
|
| 367 |
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
|
| 368 |
|
| 369 |
# Relationships
|
| 370 |
-
form: Mapped[Form] = relationship(back_populates="
|
| 371 |
user: Mapped[User] = relationship()
|
| 372 |
|
| 373 |
|
|
@@ -466,24 +475,3 @@ class WebhookConfig(Base):
|
|
| 466 |
user: Mapped[User] = relationship()
|
| 467 |
|
| 468 |
|
| 469 |
-
class ResponseChatMessage(Base):
|
| 470 |
-
"""Chat messages for response analysis conversations"""
|
| 471 |
-
__tablename__ = "response_chat_messages"
|
| 472 |
-
|
| 473 |
-
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
| 474 |
-
form_id: Mapped[int] = mapped_column(ForeignKey("forms.id"), index=True)
|
| 475 |
-
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True)
|
| 476 |
-
|
| 477 |
-
role: Mapped[str] = mapped_column(String(20)) # "user", "assistant"
|
| 478 |
-
content: Mapped[str] = mapped_column(Text)
|
| 479 |
-
|
| 480 |
-
# Metadata for analysis results
|
| 481 |
-
query_type: Mapped[str | None] = mapped_column(String(50), nullable=True) # "summary", "filter", "aggregate", "sentiment", "export"
|
| 482 |
-
sql_query: Mapped[str | None] = mapped_column(Text, nullable=True) # Generated SQL query
|
| 483 |
-
result_data: Mapped[dict | None] = mapped_column(JSON, nullable=True) # Query results or analysis data
|
| 484 |
-
|
| 485 |
-
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
|
| 486 |
-
|
| 487 |
-
# Relationships
|
| 488 |
-
form: Mapped[Form] = relationship()
|
| 489 |
-
user: Mapped[User] = relationship()
|
|
|
|
| 191 |
questions: Mapped[list["FormQuestion"]] = relationship(back_populates="form", cascade="all, delete-orphan", order_by="FormQuestion.question_order")
|
| 192 |
conditional_rules: Mapped[list["ConditionalRule"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 193 |
responses: Mapped[list["FormResponse"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 194 |
+
chat_messages: Mapped[list["ChatMessage"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 195 |
versions: Mapped[list["FormVersion"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 196 |
analytics_events: Mapped[list["FormAnalyticsEvent"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
| 197 |
uploads: Mapped[list["FormUpload"]] = relationship(back_populates="form", cascade="all, delete-orphan")
|
|
|
|
| 351 |
user: Mapped[User] = relationship(back_populates="public_forms")
|
| 352 |
|
| 353 |
|
| 354 |
+
class ChatMessage(Base):
|
| 355 |
+
"""Unified chat messages for all AI assistant conversations (form editing + response analysis)"""
|
| 356 |
+
__tablename__ = "chat_messages"
|
| 357 |
|
| 358 |
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
| 359 |
form_id: Mapped[int] = mapped_column(ForeignKey("forms.id"), index=True)
|
| 360 |
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True)
|
| 361 |
|
| 362 |
+
# Chat type: "form_editing" for add/edit, "response_analysis" for queries
|
| 363 |
+
chat_type: Mapped[str] = mapped_column(String(30), default="form_editing", index=True)
|
| 364 |
+
|
| 365 |
role: Mapped[str] = mapped_column(String(20)) # "user", "assistant"
|
| 366 |
content: Mapped[str] = mapped_column(Text)
|
| 367 |
+
query_type: Mapped[str | None] = mapped_column(String(50), nullable=True) # e.g., "add_component", "edit_component", "summary", "filter"
|
| 368 |
+
|
| 369 |
+
# Form editing specific
|
| 370 |
+
question_index: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
| 371 |
+
|
| 372 |
+
# Response analysis specific
|
| 373 |
+
sql_query: Mapped[str | None] = mapped_column(Text, nullable=True)
|
| 374 |
+
result_data: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
| 375 |
|
| 376 |
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
|
| 377 |
|
| 378 |
# Relationships
|
| 379 |
+
form: Mapped[Form] = relationship(back_populates="chat_messages")
|
| 380 |
user: Mapped[User] = relationship()
|
| 381 |
|
| 382 |
|
|
|
|
| 475 |
user: Mapped[User] = relationship()
|
| 476 |
|
| 477 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/routes/responses.py
CHANGED
|
@@ -920,7 +920,7 @@ async def delete_response(
|
|
| 920 |
# ============================================================================
|
| 921 |
|
| 922 |
from pydantic import BaseModel
|
| 923 |
-
from ..models import
|
| 924 |
from ..services.response_chat_service import response_chat_service
|
| 925 |
from ..services.agents import response_chat_module
|
| 926 |
|
|
@@ -930,11 +930,12 @@ class ResponseChatRequest(BaseModel):
|
|
| 930 |
message: str
|
| 931 |
|
| 932 |
|
| 933 |
-
class
|
| 934 |
"""Response model for a single chat message."""
|
| 935 |
id: int
|
| 936 |
role: str
|
| 937 |
content: str
|
|
|
|
| 938 |
query_type: str | None = None
|
| 939 |
sql_query: str | None = None
|
| 940 |
result_data: dict | None = None
|
|
@@ -946,7 +947,7 @@ class ResponseChatMessageResponse(BaseModel):
|
|
| 946 |
|
| 947 |
class ResponseChatResponse(BaseModel):
|
| 948 |
"""Response model for chat endpoint."""
|
| 949 |
-
message:
|
| 950 |
query_type: str
|
| 951 |
data: dict | None = None
|
| 952 |
|
|
@@ -1107,7 +1108,7 @@ async def chat_with_responses(
|
|
| 1107 |
conn.close()
|
| 1108 |
|
| 1109 |
return {
|
| 1110 |
-
"message":
|
| 1111 |
"query_type": query_type,
|
| 1112 |
"data": result_data
|
| 1113 |
}
|
|
@@ -1148,7 +1149,7 @@ async def get_response_chat_history(
|
|
| 1148 |
)
|
| 1149 |
|
| 1150 |
return {
|
| 1151 |
-
"messages": [
|
| 1152 |
"total": len(history)
|
| 1153 |
}
|
| 1154 |
|
|
@@ -1172,10 +1173,11 @@ async def clear_response_chat_history(
|
|
| 1172 |
detail="Form not found"
|
| 1173 |
)
|
| 1174 |
|
| 1175 |
-
# Delete all chat messages for this form and user
|
| 1176 |
-
db.query(
|
| 1177 |
-
|
| 1178 |
-
|
|
|
|
| 1179 |
).delete()
|
| 1180 |
db.commit()
|
| 1181 |
|
|
|
|
| 920 |
# ============================================================================
|
| 921 |
|
| 922 |
from pydantic import BaseModel
|
| 923 |
+
from ..models import ChatMessage
|
| 924 |
from ..services.response_chat_service import response_chat_service
|
| 925 |
from ..services.agents import response_chat_module
|
| 926 |
|
|
|
|
| 930 |
message: str
|
| 931 |
|
| 932 |
|
| 933 |
+
class ChatMessageResponse(BaseModel):
|
| 934 |
"""Response model for a single chat message."""
|
| 935 |
id: int
|
| 936 |
role: str
|
| 937 |
content: str
|
| 938 |
+
chat_type: str = "response_analysis"
|
| 939 |
query_type: str | None = None
|
| 940 |
sql_query: str | None = None
|
| 941 |
result_data: dict | None = None
|
|
|
|
| 947 |
|
| 948 |
class ResponseChatResponse(BaseModel):
|
| 949 |
"""Response model for chat endpoint."""
|
| 950 |
+
message: ChatMessageResponse
|
| 951 |
query_type: str
|
| 952 |
data: dict | None = None
|
| 953 |
|
|
|
|
| 1108 |
conn.close()
|
| 1109 |
|
| 1110 |
return {
|
| 1111 |
+
"message": ChatMessageResponse.model_validate(assistant_message),
|
| 1112 |
"query_type": query_type,
|
| 1113 |
"data": result_data
|
| 1114 |
}
|
|
|
|
| 1149 |
)
|
| 1150 |
|
| 1151 |
return {
|
| 1152 |
+
"messages": [ChatMessageResponse.model_validate(msg) for msg in history],
|
| 1153 |
"total": len(history)
|
| 1154 |
}
|
| 1155 |
|
|
|
|
| 1173 |
detail="Form not found"
|
| 1174 |
)
|
| 1175 |
|
| 1176 |
+
# Delete all response analysis chat messages for this form and user
|
| 1177 |
+
db.query(ChatMessage).filter(
|
| 1178 |
+
ChatMessage.form_id == form_id,
|
| 1179 |
+
ChatMessage.user_id == current_user.id,
|
| 1180 |
+
ChatMessage.chat_type == "response_analysis"
|
| 1181 |
).delete()
|
| 1182 |
db.commit()
|
| 1183 |
|
app/services/response_chat_service.py
CHANGED
|
@@ -15,7 +15,7 @@ from typing import Dict, Any, List, Optional, Tuple
|
|
| 15 |
from datetime import datetime
|
| 16 |
from sqlalchemy.orm import Session
|
| 17 |
|
| 18 |
-
from ..models import Form, FormQuestion, FormResponse, ResponseAnswer,
|
| 19 |
|
| 20 |
logger = logging.getLogger(__name__)
|
| 21 |
|
|
@@ -411,14 +411,16 @@ class ResponseChatService:
|
|
| 411 |
user_id: int,
|
| 412 |
role: str,
|
| 413 |
content: str,
|
|
|
|
| 414 |
query_type: Optional[str] = None,
|
| 415 |
sql_query: Optional[str] = None,
|
| 416 |
result_data: Optional[Dict] = None
|
| 417 |
-
) ->
|
| 418 |
"""Save a chat message to the database."""
|
| 419 |
-
message =
|
| 420 |
form_id=form_id,
|
| 421 |
user_id=user_id,
|
|
|
|
| 422 |
role=role,
|
| 423 |
content=content,
|
| 424 |
query_type=query_type,
|
|
@@ -435,22 +437,24 @@ class ResponseChatService:
|
|
| 435 |
db: Session,
|
| 436 |
form_id: int,
|
| 437 |
user_id: int,
|
|
|
|
| 438 |
limit: int = None
|
| 439 |
-
) -> List[
|
| 440 |
"""Get chat history for a form."""
|
| 441 |
limit = limit or self.max_history_messages
|
| 442 |
|
| 443 |
-
messages = db.query(
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
|
|
|
| 447 |
|
| 448 |
# Return in chronological order
|
| 449 |
return list(reversed(messages))
|
| 450 |
|
| 451 |
def format_history_for_context(
|
| 452 |
self,
|
| 453 |
-
history: List[
|
| 454 |
) -> str:
|
| 455 |
"""Format chat history as context for the AI."""
|
| 456 |
if not history:
|
|
|
|
| 15 |
from datetime import datetime
|
| 16 |
from sqlalchemy.orm import Session
|
| 17 |
|
| 18 |
+
from ..models import Form, FormQuestion, FormResponse, ResponseAnswer, ChatMessage, User
|
| 19 |
|
| 20 |
logger = logging.getLogger(__name__)
|
| 21 |
|
|
|
|
| 411 |
user_id: int,
|
| 412 |
role: str,
|
| 413 |
content: str,
|
| 414 |
+
chat_type: str = "response_analysis",
|
| 415 |
query_type: Optional[str] = None,
|
| 416 |
sql_query: Optional[str] = None,
|
| 417 |
result_data: Optional[Dict] = None
|
| 418 |
+
) -> ChatMessage:
|
| 419 |
"""Save a chat message to the database."""
|
| 420 |
+
message = ChatMessage(
|
| 421 |
form_id=form_id,
|
| 422 |
user_id=user_id,
|
| 423 |
+
chat_type=chat_type,
|
| 424 |
role=role,
|
| 425 |
content=content,
|
| 426 |
query_type=query_type,
|
|
|
|
| 437 |
db: Session,
|
| 438 |
form_id: int,
|
| 439 |
user_id: int,
|
| 440 |
+
chat_type: str = "response_analysis",
|
| 441 |
limit: int = None
|
| 442 |
+
) -> List[ChatMessage]:
|
| 443 |
"""Get chat history for a form."""
|
| 444 |
limit = limit or self.max_history_messages
|
| 445 |
|
| 446 |
+
messages = db.query(ChatMessage).filter(
|
| 447 |
+
ChatMessage.form_id == form_id,
|
| 448 |
+
ChatMessage.user_id == user_id,
|
| 449 |
+
ChatMessage.chat_type == chat_type
|
| 450 |
+
).order_by(ChatMessage.created_at.desc()).limit(limit).all()
|
| 451 |
|
| 452 |
# Return in chronological order
|
| 453 |
return list(reversed(messages))
|
| 454 |
|
| 455 |
def format_history_for_context(
|
| 456 |
self,
|
| 457 |
+
history: List[ChatMessage]
|
| 458 |
) -> str:
|
| 459 |
"""Format chat history as context for the AI."""
|
| 460 |
if not history:
|