Feature/memory work (#68)
* feat(memory): add conversation title to conversation list response for frontend display * feat(memory): optimize conversation retrieval, enable working memory to return conversation question summaries * fix(memory): fix conversation re-generation logic * style(desc): improve description of get_conversation function
This commit is contained in:
@@ -2,14 +2,13 @@ import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
from app.core.response_utils import success, fail
|
||||
|
||||
from app.core.logging_config import get_api_logger
|
||||
from app.core.response_utils import success
|
||||
from app.db import get_db
|
||||
from app.dependencies import get_current_user
|
||||
from app.models import User
|
||||
from app.schemas.response_schema import ApiResponse
|
||||
from app.services import conversation_service
|
||||
from app.services.conversation_service import ConversationService
|
||||
|
||||
api_logger = get_api_logger()
|
||||
@@ -54,8 +53,7 @@ def get_conversations(
|
||||
"""
|
||||
conversation_service = ConversationService(db)
|
||||
conversations = conversation_service.get_user_conversations(
|
||||
group_id,
|
||||
current_user.current_workspace_id
|
||||
group_id
|
||||
)
|
||||
return success(data=[
|
||||
{
|
||||
@@ -88,9 +86,17 @@ def get_messages(
|
||||
- Logging can be added for audit and debugging.
|
||||
"""
|
||||
conversation_service = ConversationService(db)
|
||||
messages = conversation_service.get_conversation_history(
|
||||
messages_obj = conversation_service.get_messages(
|
||||
conversation_id,
|
||||
)
|
||||
messages = [
|
||||
{
|
||||
"role": message.role,
|
||||
"content": message.content,
|
||||
"created_at": int(message.created_at.timestamp() * 1000),
|
||||
}
|
||||
for message in messages_obj
|
||||
]
|
||||
return success(data=messages, msg="get conversation history success")
|
||||
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@ class ConversationDetail(Base):
|
||||
conversation_id = Column(UUID(as_uuid=True), ForeignKey("conversations.id"))
|
||||
|
||||
theme = Column(String, comment="会话主题")
|
||||
theme_intro = Column(String, comment="主题介绍")
|
||||
summary = Column(String, comment="会话摘要")
|
||||
takeaways = Column(JSON, comment="会话要点")
|
||||
question = Column(JSON, comment="用户问题")
|
||||
info_score = Column(Integer, comment="会话信息量评分")
|
||||
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class ChatResponse(BaseModel):
|
||||
# ---------- Conversation Summary Schemas ----------
|
||||
class ConversationOut(BaseModel):
|
||||
theme: str
|
||||
theme_intro: str
|
||||
question: list[str]
|
||||
summary: str
|
||||
takeaways: list[str]
|
||||
info_score: int
|
||||
|
||||
@@ -118,25 +118,22 @@ class ConversationService:
|
||||
|
||||
def get_user_conversations(
|
||||
self,
|
||||
user_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
user_id: uuid.UUID
|
||||
) -> list[Conversation]:
|
||||
"""
|
||||
Retrieve recent conversations for a specific user within a workspace.
|
||||
Retrieve recent conversations for a specific user
|
||||
|
||||
This method delegates persistence logic to the repository layer and
|
||||
applies service-level defaults (e.g. recent conversation limit).
|
||||
|
||||
Args:
|
||||
user_id (uuid.UUID): Unique identifier of the user.
|
||||
workspace_id (uuid.UUID): Workspace scope for the query.
|
||||
|
||||
Returns:
|
||||
list[Conversation]: A list of recent conversation entities.
|
||||
"""
|
||||
conversations = self.conversation_repo.get_conversation_by_user_id(
|
||||
user_id,
|
||||
workspace_id,
|
||||
limit=10
|
||||
)
|
||||
return conversations
|
||||
@@ -465,11 +462,17 @@ class ConversationService:
|
||||
conversation = self.get_conversation(
|
||||
conversation_id=conversation_id,
|
||||
)
|
||||
if conversation_detail:
|
||||
if not conversation:
|
||||
raise BusinessException("Conversation not found", BizCode.INVALID_CONVERSATION)
|
||||
is_stable = (
|
||||
conversation.updated_at
|
||||
and datetime.now() - conversation.updated_at > timedelta(days=1)
|
||||
)
|
||||
if conversation_detail and is_stable:
|
||||
logger.info(f"Conversation detail found in repository for conversation_id={conversation_id}")
|
||||
return ConversationOut(
|
||||
theme=conversation_detail.theme,
|
||||
theme_intro=conversation_detail.theme_intro,
|
||||
question=conversation_detail.question if conversation_detail.question else [],
|
||||
summary=conversation_detail.summary,
|
||||
takeaways=conversation_detail.takeaways,
|
||||
info_score=conversation_detail.info_score,
|
||||
@@ -526,6 +529,7 @@ class ConversationService:
|
||||
language=language,
|
||||
conversation=str(conversation_messages)
|
||||
)
|
||||
|
||||
messages = [
|
||||
(RoleType.SYSTEM, rendered_system_message),
|
||||
(RoleType.USER, rendered_user_message),
|
||||
@@ -547,28 +551,37 @@ class ConversationService:
|
||||
|
||||
summary = result.get('summary', "")
|
||||
theme = result.get('theme', "")
|
||||
theme_intro = result.get("theme_intro", "")
|
||||
question = result.get("question") or []
|
||||
takeaways = result.get("takeaways") or []
|
||||
info_score = result.get("info_score", 50)
|
||||
|
||||
if datetime.now() - conversation.updated_at > timedelta(days=1):
|
||||
logger.info(f"Updating conversation detail in DB for conversation_id={conversation_id}")
|
||||
conversation_detail = ConversationDetail(
|
||||
conversation_id=conversation.id,
|
||||
summary=summary,
|
||||
theme=theme,
|
||||
theme_intro=theme_intro,
|
||||
takeaways=takeaways,
|
||||
info_score=info_score
|
||||
)
|
||||
self.conversation_repo.add_conversation_detail(conversation_detail)
|
||||
if not is_stable:
|
||||
if not conversation_detail:
|
||||
logger.info(f"Creating conversation detail in DB for conversation_id={conversation_id}")
|
||||
conversation_detail = ConversationDetail(
|
||||
conversation_id=conversation.id,
|
||||
summary=summary,
|
||||
theme=theme,
|
||||
question=question,
|
||||
takeaways=takeaways,
|
||||
info_score=info_score
|
||||
)
|
||||
self.conversation_repo.add_conversation_detail(conversation_detail)
|
||||
else:
|
||||
logger.info(f"Updating conversation detail in DB for conversation_id={conversation_id}")
|
||||
conversation_detail.summary = summary
|
||||
conversation_detail.theme = theme
|
||||
conversation_detail.question = question
|
||||
conversation_detail.takeaways = takeaways
|
||||
conversation_detail.info_score = info_score
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(conversation_detail)
|
||||
|
||||
logger.info(f"Returning conversation summary for conversation_id={conversation_id}")
|
||||
conversation_out = ConversationOut(
|
||||
theme=theme,
|
||||
theme_intro=theme_intro,
|
||||
question=question,
|
||||
summary=summary,
|
||||
takeaways=takeaways,
|
||||
info_score=info_score
|
||||
|
||||
@@ -33,18 +33,18 @@ You are a professional dialogue content summarizer, specializing in extracting c
|
||||
- Language: Please strictly output content in the language specified by the <Language> tag.
|
||||
- Structure: JSON object with five fields,:
|
||||
1. `theme`: A concise phrase describing the conversation’s core topic (e.g., "inquiry about delivery time rules");
|
||||
2. `theme_intro`: A brief explanation of the conversation’s core theme to clarify its specific scope and focus (e.g., "The conversation focuses on the user's inquiry about delivery time standards for regular and remote areas");
|
||||
3. `summary`: A single sentence including "user request + AI response + interaction logic" (≤100 words);
|
||||
4. `takeaways`: A list of brief bullet-point takeaways summarizing the key points from the conversation (e.g., ["User clarified delivery time differences between regular and remote areas"]).
|
||||
2. `summary`: A single sentence including "user request + AI response + interaction logic" (≤150 words);
|
||||
3. `takeaways`: A list of brief bullet-point takeaways summarizing the key points from the conversation (e.g., ["User clarified delivery time differences between regular and remote areas"]).
|
||||
4. `question`: A list of brief declarative statements summarizing the pitfalls the user encountered during the current conversation.Return an empty list if none are present.
|
||||
5. `info_score`: Numerical score (0–100) representing conversation information richness.
|
||||
- Language Style: Concise, objective, conversational (avoid overly formal terms).
|
||||
|
||||
# Example JSON Output
|
||||
{
|
||||
"theme": string,
|
||||
"theme_intro": string,
|
||||
"summary": string,
|
||||
"takeaways": array[string],
|
||||
"question": array[string]
|
||||
"info_score": 85
|
||||
}
|
||||
{% endraw %}
|
||||
|
||||
Reference in New Issue
Block a user