Automatically generate titles for conversations from history

This commit is contained in:
sabaimran 2024-11-08 16:02:34 -08:00
parent 7159b0b735
commit 807687a0ac
6 changed files with 84 additions and 5 deletions

View file

@ -8,7 +8,7 @@ import ChatHistory from "../components/chatHistory/chatHistory";
import { useSearchParams } from "next/navigation";
import Loading from "../components/loading/loading";
import { processMessageChunk } from "../common/chatFunctions";
import { generateNewTitle, processMessageChunk } from "../common/chatFunctions";
import "katex/dist/katex.min.css";
@ -244,6 +244,9 @@ export default function Chat() {
setQueryToProcess("");
setProcessQuerySignal(false);
setImages([]);
if (conversationId) generateNewTitle(conversationId, setTitle);
break;
}

View file

@ -319,6 +319,23 @@ export async function packageFilesForUpload(files: FileList): Promise<FormData>
return formData;
}
export function generateNewTitle(conversationId: string, setTitle: (title: string) => void) {
fetch(`/api/chat/title?conversation_id=${conversationId}`, {
method: "POST",
})
.then((res) => {
if (!res.ok) throw new Error(`Failed to call API with error ${res.statusText}`);
return res.json();
})
.then((data) => {
setTitle(data.title);
})
.catch((err) => {
console.error(err);
return;
});
}
export function uploadDataForIndexing(
files: FileList,
setWarning: (warning: string) => void,

View file

@ -458,7 +458,11 @@ class Conversation(BaseModel):
user = models.ForeignKey(KhojUser, on_delete=models.CASCADE)
conversation_log = models.JSONField(default=dict)
client = models.ForeignKey(ClientApplication, on_delete=models.CASCADE, default=None, null=True, blank=True)
# Slug is an app-generated conversation identifier. Need not be unique. Used as display title essentially.
slug = models.CharField(max_length=200, default=None, null=True, blank=True)
# The title field is explicitly set by the user.
title = models.CharField(max_length=200, default=None, null=True, blank=True)
agent = models.ForeignKey(Agent, on_delete=models.SET_NULL, default=None, null=True, blank=True)
file_filters = models.JSONField(default=list)

View file

@ -988,16 +988,27 @@ You are an extremely smart and helpful title generator assistant. Given a user q
# Examples:
User: Show a new Calvin and Hobbes quote every morning at 9am. My Current Location: Shanghai, China
Khoj: Your daily Calvin and Hobbes Quote
Assistant: Your daily Calvin and Hobbes Quote
User: Notify me when version 2.0.0 of the sentence transformers python package is released. My Current Location: Mexico City, Mexico
Khoj: Sentence Transformers Python Package Version 2.0.0 Release
Assistant: Sentence Transformers Python Package Version 2.0.0 Release
User: Gather the latest tech news on the first sunday of every month.
Khoj: Your Monthly Dose of Tech News
Assistant: Your Monthly Dose of Tech News
User Query: {query}
Khoj:
Assistant:
""".strip()
)
conversation_title_generation = PromptTemplate.from_template(
"""
You are an extremely smart and helpful title generator assistant. Given a conversation, extract the subject of the conversation. Crisp, informative, ten words or less.
Conversation History:
{chat_history}
Assistant:
""".strip()
)

View file

@ -40,6 +40,7 @@ from khoj.routers.helpers import (
ConversationCommandRateLimiter,
DeleteMessageRequestBody,
FeedbackData,
acreate_title_from_history,
agenerate_chat_response,
aget_relevant_information_sources,
aget_relevant_output_modes,
@ -530,6 +531,32 @@ async def set_conversation_title(
)
@api_chat.post("/title")
@requires(["authenticated"])
async def generate_chat_title(
request: Request,
common: CommonQueryParams,
conversation_id: str,
):
user: KhojUser = request.user.object
conversation = await ConversationAdapters.aget_conversation_by_user(user=user, conversation_id=conversation_id)
# Conversation.title is explicitly set by the user. Do not override.
if conversation.title:
return {"status": "ok", "title": conversation.title}
if not conversation:
raise HTTPException(status_code=404, detail="Conversation not found")
new_title = await acreate_title_from_history(request.user.object, conversation=conversation)
conversation.slug = new_title
conversation.asave()
return {"status": "ok", "title": new_title}
@api_chat.delete("/conversation/message", response_class=Response)
@requires(["authenticated"])
def delete_message(request: Request, delete_request: DeleteMessageRequestBody) -> Response:

View file

@ -299,6 +299,23 @@ def construct_chat_history(conversation_history: dict, n: int = 4, agent_name="A
return chat_history
async def acreate_title_from_history(
user: KhojUser,
conversation: Conversation,
):
"""
Create a title from the given conversation history
"""
chat_history = construct_chat_history(conversation.conversation_log)
title_generation_prompt = prompts.conversation_title_generation.format(chat_history=chat_history)
with timer("Chat actor: Generate title from conversation history", logger):
response = await send_message_to_model_wrapper(title_generation_prompt, user=user)
return response.strip()
async def acreate_title_from_query(query: str, user: KhojUser = None) -> str:
"""
Create a title from the given query