diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts
index 0929e0a69..3892afeb8 100644
--- a/src/lib/apis/index.ts
+++ b/src/lib/apis/index.ts
@@ -539,7 +539,7 @@ export const updateTaskConfig = async (token: string, config: object) => {
export const generateTitle = async (
token: string = '',
model: string,
- messages: string[],
+ messages: object[],
chat_id?: string
) => {
let error = null;
@@ -573,7 +573,39 @@ export const generateTitle = async (
throw error;
}
- return res?.choices[0]?.message?.content.replace(/["']/g, '') ?? 'New Chat';
+ try {
+ // Step 1: Safely extract the response string
+ const response = res?.choices[0]?.message?.content ?? '';
+
+ // Step 2: Attempt to fix common JSON format issues like single quotes
+ const sanitizedResponse = response.replace(/['‘’`]/g, '"'); // Convert single quotes to double quotes for valid JSON
+
+ // Step 3: Find the relevant JSON block within the response
+ const jsonStartIndex = sanitizedResponse.indexOf('{');
+ const jsonEndIndex = sanitizedResponse.lastIndexOf('}');
+
+ // Step 4: Check if we found a valid JSON block (with both `{` and `}`)
+ if (jsonStartIndex !== -1 && jsonEndIndex !== -1) {
+ const jsonResponse = sanitizedResponse.substring(jsonStartIndex, jsonEndIndex + 1);
+
+ // Step 5: Parse the JSON block
+ const parsed = JSON.parse(jsonResponse);
+
+ // Step 6: If there's a "tags" key, return the tags array; otherwise, return an empty array
+ if (parsed && parsed.title) {
+ return parsed.title;
+ } else {
+ return null;
+ }
+ }
+
+ // If no valid JSON block found, return an empty array
+ return null;
+ } catch (e) {
+ // Catch and safely return empty array on any parsing errors
+ console.error('Failed to parse response: ', e);
+ return null;
+ }
};
export const generateTags = async (
diff --git a/src/lib/components/layout/Sidebar/ChatItem.svelte b/src/lib/components/layout/Sidebar/ChatItem.svelte
index 19c19cd3d..9e1476310 100644
--- a/src/lib/components/layout/Sidebar/ChatItem.svelte
+++ b/src/lib/components/layout/Sidebar/ChatItem.svelte
@@ -39,6 +39,7 @@
import XMark from '$lib/components/icons/XMark.svelte';
import Document from '$lib/components/icons/Document.svelte';
import Sparkles from '$lib/components/icons/Sparkles.svelte';
+ import { generateTitle } from '$lib/apis';
export let className = '';
@@ -136,6 +137,7 @@
let itemElement;
+ let generating = false;
let doubleClicked = false;
let dragged = false;
let x = 0;
@@ -223,6 +225,40 @@
if (input) input.focus();
}, 0);
};
+
+ const generateTitleHandler = async () => {
+ generating = true;
+ if (!chat) {
+ chat = await getChatById(localStorage.token, id);
+ }
+
+ const messages = (chat.chat?.messages ?? []).map((message) => {
+ return {
+ role: message.role,
+ content: message.content
+ };
+ });
+
+ const model = chat.chat.models.at(0) ?? chat.models.at(0) ?? '';
+
+ chatTitle = '';
+
+ const generatedTitle = await generateTitle(localStorage.token, model, messages).catch(
+ (error) => {
+ toast.error(`${error}`);
+ return null;
+ }
+ );
+
+ if (generatedTitle) {
+ if (generatedTitle !== title) {
+ editChatTitle(id, generatedTitle);
+ }
+
+ confirmEdit = false;
+ }
+ generating = false;
+ };
@@ -264,14 +300,22 @@
? 'bg-gray-200 dark:bg-gray-900'
: selected
? 'bg-gray-100 dark:bg-gray-950'
- : 'group-hover:bg-gray-100 dark:group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
+ : 'group-hover:bg-gray-100 dark:group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis relative {generating
+ ? 'cursor-not-allowed'
+ : ''}"
>
{
+ // check if target is generate button
+ if (e.relatedTarget?.id === 'generate-title-button') {
+ return;
+ }
+
if (doubleClicked) {
e.preventDefault();
e.stopPropagation();
@@ -360,7 +404,17 @@
class="flex self-center items-center space-x-1.5 z-10 translate-y-[0.5px] -translate-x-[0.5px]"
>
-