From 59724ea9d80c5502f23b4b0545611a45aebbd759 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Wed, 17 Jan 2024 15:09:38 -0800 Subject: [PATCH 01/29] feat: message ts --- src/routes/(app)/+page.svelte | 9 ++++++--- src/routes/(app)/c/[id]/+page.svelte | 26 ++++++++++++-------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index 70320093..2a13d020 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -145,7 +145,8 @@ role: 'user', user: _user ?? undefined, content: userPrompt, - files: files.length > 0 ? files : undefined + files: files.length > 0 ? files : undefined, + timestamp: Date.now() }; // Add message to history and Set currentId to messageId @@ -256,7 +257,8 @@ childrenIds: [], role: 'assistant', content: '', - model: model + model: model, + timestamp: Date.now() }; // Add message to history and Set currentId to messageId @@ -446,7 +448,8 @@ childrenIds: [], role: 'assistant', content: '', - model: model + model: model, + timestamp: Date.now() }; history.messages[responseMessageId] = responseMessage; diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte index c4774aaa..fffdd213 100644 --- a/src/routes/(app)/c/[id]/+page.svelte +++ b/src/routes/(app)/c/[id]/+page.svelte @@ -135,8 +135,7 @@ // Ollama functions ////////////////////////// - const submitPrompt = async (userPrompt, user) => { - console.log(userPrompt, user); + const submitPrompt = async (userPrompt, _user = null) => { console.log('submitPrompt', $chatId); if (selectedModels.includes('')) { @@ -163,8 +162,10 @@ parentId: messages.length !== 0 ? messages.at(-1).id : null, childrenIds: [], role: 'user', + user: _user ?? undefined, content: userPrompt, - files: files.length > 0 ? files : undefined + files: files.length > 0 ? files : undefined, + timestamp: Date.now() }; // Add message to history and Set currentId to messageId @@ -200,15 +201,7 @@ await chatId.set('local'); } await tick(); - } else if (chat.chat["models"] != selectedModels) { - // If model is not saved in DB, then save selectedmodel when message is sent - - chat = await updateChatById(localStorage.token, $chatId, { - models: selectedModels - }); - await chats.set(await getChatList(localStorage.token)); } - // Reset chat input textarea prompt = ''; files = []; @@ -282,7 +275,8 @@ childrenIds: [], role: 'assistant', content: '', - model: model + model: model, + timestamp: Date.now() }; // Add message to history and Set currentId to messageId @@ -472,7 +466,8 @@ childrenIds: [], role: 'assistant', content: '', - model: model + model: model, + timestamp: Date.now() }; history.messages[responseMessageId] = responseMessage; @@ -703,7 +698,10 @@
- 0 && !selectedModels.includes('')} /> + 0 && !selectedModels.includes('')} + />
From 4ec81a897e2de087bf316d5c4046db35f816217d Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Wed, 17 Jan 2024 17:43:45 -0800 Subject: [PATCH 02/29] feat: message ts display --- src/lib/components/chat/Messages/Name.svelte | 2 +- src/lib/components/chat/Messages/ResponseMessage.svelte | 7 +++++++ src/lib/components/chat/Messages/UserMessage.svelte | 8 ++++++++ src/routes/(app)/+page.svelte | 6 +++--- src/routes/(app)/c/[id]/+page.svelte | 6 +++--- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/lib/components/chat/Messages/Name.svelte b/src/lib/components/chat/Messages/Name.svelte index 4638e3b7..dea6ef84 100644 --- a/src/lib/components/chat/Messages/Name.svelte +++ b/src/lib/components/chat/Messages/Name.svelte @@ -1,3 +1,3 @@ -
+
diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 4dfcd61d..aa3f30b5 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -1,4 +1,5 @@ + + +
+ + +
+
or
+ + +
+
+
diff --git a/src/lib/components/common/Modal.svelte b/src/lib/components/common/Modal.svelte index 0fb3f8bd..b292b910 100644 --- a/src/lib/components/common/Modal.svelte +++ b/src/lib/components/common/Modal.svelte @@ -8,7 +8,9 @@ let mounted = false; const sizeToWidth = (size) => { - if (size === 'sm') { + if (size === 'xs') { + return 'w-[16rem]'; + } else if (size === 'sm') { return 'w-[30rem]'; } else { return 'w-[40rem]'; diff --git a/src/lib/components/layout/Navbar.svelte b/src/lib/components/layout/Navbar.svelte index bd52bd9b..e75dafd6 100644 --- a/src/lib/components/layout/Navbar.svelte +++ b/src/lib/components/layout/Navbar.svelte @@ -5,11 +5,14 @@ import { getChatById } from '$lib/apis/chats'; import { chatId, modelfiles } from '$lib/stores'; + import ShareChatModal from '../chat/ShareChatModal.svelte'; export let initNewChat: Function; export let title: string = 'Ollama Web UI'; export let shareEnabled: boolean = false; + let showShareChatModal = false; + const shareChat = async () => { const chat = (await getChatById(localStorage.token, $chatId)).chat; console.log('share', chat); @@ -53,16 +56,17 @@ }; + From 93ec04003a05b146161de0c99ee6f376e918e2e0 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Wed, 17 Jan 2024 21:03:53 -0800 Subject: [PATCH 09/29] fix: share modal styling --- src/lib/components/chat/ShareChatModal.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/ShareChatModal.svelte b/src/lib/components/chat/ShareChatModal.svelte index f834c421..3e9e30c4 100644 --- a/src/lib/components/chat/ShareChatModal.svelte +++ b/src/lib/components/chat/ShareChatModal.svelte @@ -21,10 +21,10 @@
-
or
+
or
From 287668f84ea295ace9517960029c048b1a98d5a4 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Wed, 17 Jan 2024 22:49:58 -0800 Subject: [PATCH 11/29] feat: tag add/remove frontend --- src/lib/components/layout/Navbar.svelte | 127 +++++++++++++++++++++--- 1 file changed, 112 insertions(+), 15 deletions(-) diff --git a/src/lib/components/layout/Navbar.svelte b/src/lib/components/layout/Navbar.svelte index e75dafd6..521e7abc 100644 --- a/src/lib/components/layout/Navbar.svelte +++ b/src/lib/components/layout/Navbar.svelte @@ -6,6 +6,7 @@ import { getChatById } from '$lib/apis/chats'; import { chatId, modelfiles } from '$lib/stores'; import ShareChatModal from '../chat/ShareChatModal.svelte'; + import { stringify } from 'postcss'; export let initNewChat: Function; export let title: string = 'Ollama Web UI'; @@ -13,6 +14,24 @@ let showShareChatModal = false; + let tags = [ + // { + // name: 'general' + // }, + // { + // name: 'medicine' + // }, + // { + // name: 'cooking' + // }, + // { + // name: 'education' + // } + ]; + + let tagName = ''; + let showTagInput = false; + const shareChat = async () => { const chat = (await getChatById(localStorage.token, $chatId)).chat; console.log('share', chat); @@ -54,6 +73,20 @@ saveAs(blob, `chat-${chat.title}.txt`); }; + + const addTag = () => { + if (!tags.find((e) => e.name === tagName)) { + tags = [ + ...tags, + { + name: JSON.parse(JSON.stringify(tagName)) + } + ]; + } + + tagName = ''; + showTagInput = false; + }; @@ -93,23 +126,87 @@
-
-
-
Add Tags
-
- + {#each tags as tag} +
+
+ {tag.name} +
+
+ {/each} + +
+ {#if showTagInput} +
+ + + +
+ + + {/if} + +
From 077f1fa34bea06f61545feea4775264b7b6a7792 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Thu, 18 Jan 2024 00:58:45 -0800 Subject: [PATCH 12/29] feat: convo tagging backend support --- backend/apps/web/models/tags.py | 180 ++++++++++++++++++++++++++++++ backend/apps/web/routers/chats.py | 65 +++++++++++ 2 files changed, 245 insertions(+) create mode 100644 backend/apps/web/models/tags.py diff --git a/backend/apps/web/models/tags.py b/backend/apps/web/models/tags.py new file mode 100644 index 00000000..eb33b31b --- /dev/null +++ b/backend/apps/web/models/tags.py @@ -0,0 +1,180 @@ +from pydantic import BaseModel +from typing import List, Union, Optional +from peewee import * +from playhouse.shortcuts import model_to_dict + +import json +import uuid +import time + +from apps.web.internal.db import DB + +#################### +# Tag DB Schema +#################### + + +class Tag(Model): + name = CharField(unique=True) + user_id = CharField() + data = TextField(null=True) + + class Meta: + database = DB + + +class ChatIdTag(Model): + tag_name = ForeignKeyField(Tag, backref="chat_id_tags") + chat_id = CharField() + user_id = CharField() + timestamp = DateField() + + class Meta: + database = DB + + +class TagModel(BaseModel): + name: str + user_id: str + data: Optional[str] = None + + +class ChatIdTagModel(BaseModel): + tag_name: str + chat_id: str + user_id: str + timestamp: int + + +#################### +# Forms +#################### + + +class ChatIdTagForm(BaseModel): + tag_name: str + chat_id: str + + +class TagChatIdsResponse(BaseModel): + chat_ids: List[str] + + +class ChatTagsResponse(BaseModel): + tags: List[str] + + +class TagTable: + def __init__(self, db): + self.db = db + db.create_tables([Tag, ChatIdTag]) + + def insert_new_tag(self, name: str, user_id: str) -> Optional[TagModel]: + tag = TagModel(**{"user_id": user_id, "name": name}) + try: + result = Tag.create(**tag.model_dump()) + if result: + return tag + else: + return None + except: + return None + + def get_tag_by_name_and_user_id( + self, name: str, user_id: str + ) -> Optional[TagModel]: + try: + tag = Tag.get(Tag.name == name, Tag.user_id == user_id) + return TagModel(**model_to_dict(tag)) + except: + return None + + def add_tag_to_chat( + self, user_id: str, form_data: ChatIdTagForm + ) -> Optional[ChatTagsResponse]: + tag = self.get_tag_by_name_and_user_id(form_data.tag_name, user_id) + if tag == None: + tag = self.insert_new_tag(form_data.tag_name, user_id) + + chatIdTag = ChatIdTagModel(**{"user_id": user_id, "tag_name": tag.name}) + try: + result = ChatIdTag.create(**chatIdTag.model_dump()) + if result: + return chatIdTag + else: + return None + except: + return None + + def get_tags_by_chat_id_and_user_id( + self, chat_id: str, user_id: str + ) -> List[TagModel]: + return [ + TagModel(**model_to_dict(tag)) + for tag in Tag.select().where( + Tag.name + in [ + ChatIdTagModel(**model_to_dict(chat_id_tag)).tag_name + for chat_id_tag in ChatIdTag.select() + .where( + (ChatIdTag.user_id == user_id) & (ChatIdTag.chat_id == chat_id) + ) + .order_by(ChatIdTag.timestamp.desc()) + ] + ) + ] + + def get_chat_ids_by_tag_name_and_user_id( + self, tag_name: str, user_id: str + ) -> Optional[ChatIdTagModel]: + return [ + ChatIdTagModel(**model_to_dict(chat_id_tag)) + for chat_id_tag in ChatIdTag.select() + .where((ChatIdTag.user_id == user_id) & (ChatIdTag.tag_name == tag_name)) + .order_by(ChatIdTag.timestamp.desc()) + ] + + def count_chat_ids_by_tag_name_and_user_id( + self, tag_name: str, user_id: str + ) -> int: + return ( + ChatIdTag.select() + .where((ChatIdTag.tag_name == tag_name) & (ChatIdTag.user_id == user_id)) + .count() + ) + + def delete_tag_by_tag_name_and_chat_id_and_user_id( + self, tag_name: str, chat_id: str, user_id: str + ) -> bool: + try: + query = ChatIdTag.delete().where( + (ChatIdTag.tag_name == tag_name) + & (ChatIdTag.chat_id == chat_id) + & (ChatIdTag.user_id == user_id) + ) + query.execute() # Remove the rows, return number of rows removed. + + tag_count = self.count_chat_ids_by_tag_name_and_user_id(tag_name, user_id) + if tag_count == 0: + # Remove tag item from Tag col as well + query = Tag.delete().where( + (Tag.name == tag_name) & (Tag.user_id == user_id) + ) + query.execute() # Remove the rows, return number of rows removed. + + return True + except: + return False + + def delete_tags_by_chat_id_and_user_id(self, chat_id: str, user_id: str) -> bool: + tags = self.get_tags_by_chat_id_and_user_id(chat_id, user_id) + + for tag in tags: + self.delete_tag_by_tag_name_and_chat_id_and_user_id( + tag.tag_name, chat_id, user_id + ) + + return True + + +Tags = TagTable(DB) diff --git a/backend/apps/web/routers/chats.py b/backend/apps/web/routers/chats.py index e97e1473..a419a412 100644 --- a/backend/apps/web/routers/chats.py +++ b/backend/apps/web/routers/chats.py @@ -16,6 +16,14 @@ from apps.web.models.chats import ( Chats, ) + +from apps.web.models.tags import ( + TagModel, + ChatIdTagForm, + ChatTagsResponse, + Tags, +) + from utils.utils import ( bearer_scheme, ) @@ -115,6 +123,63 @@ async def delete_chat_by_id(id: str, user=Depends(get_current_user)): return result +############################ +# GetChatTagsById +############################ + + +@router.get("/{id}/tags", response_model=List[TagModel]) +async def get_chat_tags_by_id(id: str, user=Depends(get_current_user)): + tags = Tags.get_tags_by_chat_id_and_user_id(id, user.id) + + if tags: + return tags + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND + ) + + +############################ +# AddChatTagById +############################ + + +@router.post("/{id}/tags", response_model=Optional[ChatTagsResponse]) +async def add_chat_tag_by_id( + id: str, form_data: ChatIdTagForm, user=Depends(get_current_user) +): + tag = Tags.add_tag_to_chat(user.id, {"tag_name": form_data.tag_name, "chat_id": id}) + + if tag: + return tag + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND + ) + + +############################ +# DeleteChatTagById +############################ + + +@router.delete("/{id}/tags", response_model=Optional[bool]) +async def add_chat_tag_by_id( + id: str, form_data: ChatIdTagForm, user=Depends(get_current_user) +): + tag = Tags.delete_tag_by_tag_name_and_chat_id_and_user_id( + form_data.tag_name, id, user.id + ) + + if tag: + return tag + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND + ) + + ############################ # DeleteAllChats ############################ From d5ed119687cfb8a27e089dc185ba563372e94189 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Thu, 18 Jan 2024 01:04:24 -0800 Subject: [PATCH 13/29] feat: convo tagging api added --- backend/apps/web/routers/chats.py | 25 +++++- src/lib/apis/chats/index.ts | 135 ++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 4 deletions(-) diff --git a/backend/apps/web/routers/chats.py b/backend/apps/web/routers/chats.py index a419a412..0c9aa573 100644 --- a/backend/apps/web/routers/chats.py +++ b/backend/apps/web/routers/chats.py @@ -165,15 +165,32 @@ async def add_chat_tag_by_id( @router.delete("/{id}/tags", response_model=Optional[bool]) -async def add_chat_tag_by_id( +async def delete_chat_tag_by_id( id: str, form_data: ChatIdTagForm, user=Depends(get_current_user) ): - tag = Tags.delete_tag_by_tag_name_and_chat_id_and_user_id( + result = Tags.delete_tag_by_tag_name_and_chat_id_and_user_id( form_data.tag_name, id, user.id ) - if tag: - return tag + if result: + return result + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND + ) + + +############################ +# DeleteAllChatTagsById +############################ + + +@router.delete("/{id}/tags/all", response_model=Optional[bool]) +async def delete_all_chat_tags_by_id(id: str, user=Depends(get_current_user)): + result = Tags.delete_tags_by_chat_id_and_user_id(id, user.id) + + if result: + return result else: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts index 0eddf5b4..b7f01c6e 100644 --- a/src/lib/apis/chats/index.ts +++ b/src/lib/apis/chats/index.ts @@ -192,6 +192,141 @@ export const deleteChatById = async (token: string, id: string) => { return res; }; +export const getTagsById = async (token: string, id: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/tags`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err; + + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + +export const addTagById = async (token: string, id: string, tagName: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/tags`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify({ + tag_name: tagName, + chat_id: id + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err; + + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + +export const deleteTagById = async (token: string, id: string, tagName: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/tags`, { + method: 'DELETE', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify({ + tag_name: tagName, + chat_id: id + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err; + + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; +export const deleteTagsById = async (token: string, id: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/tags/all`, { + method: 'DELETE', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err; + + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const deleteAllChats = async (token: string) => { let error = null; From 987685dbf9223197d66c77fac82033fb7bfd2528 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Thu, 18 Jan 2024 02:10:07 -0800 Subject: [PATCH 14/29] feat: convo tagging full integration --- backend/apps/web/models/tags.py | 55 +++++--- backend/apps/web/routers/chats.py | 22 +++- src/lib/components/layout/Navbar.svelte | 168 +++++++++++------------- src/routes/(app)/+page.svelte | 28 +++- src/routes/(app)/c/[id]/+page.svelte | 31 ++++- 5 files changed, 185 insertions(+), 119 deletions(-) diff --git a/backend/apps/web/models/tags.py b/backend/apps/web/models/tags.py index eb33b31b..ef21ca08 100644 --- a/backend/apps/web/models/tags.py +++ b/backend/apps/web/models/tags.py @@ -15,7 +15,8 @@ from apps.web.internal.db import DB class Tag(Model): - name = CharField(unique=True) + id = CharField(unique=True) + name = CharField() user_id = CharField() data = TextField(null=True) @@ -24,7 +25,8 @@ class Tag(Model): class ChatIdTag(Model): - tag_name = ForeignKeyField(Tag, backref="chat_id_tags") + id = CharField(unique=True) + tag_name = CharField() chat_id = CharField() user_id = CharField() timestamp = DateField() @@ -34,12 +36,14 @@ class ChatIdTag(Model): class TagModel(BaseModel): + id: str name: str user_id: str data: Optional[str] = None class ChatIdTagModel(BaseModel): + id: str tag_name: str chat_id: str user_id: str @@ -70,14 +74,15 @@ class TagTable: db.create_tables([Tag, ChatIdTag]) def insert_new_tag(self, name: str, user_id: str) -> Optional[TagModel]: - tag = TagModel(**{"user_id": user_id, "name": name}) + id = str(uuid.uuid4()) + tag = TagModel(**{"id": id, "user_id": user_id, "name": name}) try: result = Tag.create(**tag.model_dump()) if result: return tag else: return None - except: + except Exception as e: return None def get_tag_by_name_and_user_id( @@ -86,17 +91,27 @@ class TagTable: try: tag = Tag.get(Tag.name == name, Tag.user_id == user_id) return TagModel(**model_to_dict(tag)) - except: + except Exception as e: return None def add_tag_to_chat( self, user_id: str, form_data: ChatIdTagForm - ) -> Optional[ChatTagsResponse]: + ) -> Optional[ChatIdTagModel]: tag = self.get_tag_by_name_and_user_id(form_data.tag_name, user_id) if tag == None: tag = self.insert_new_tag(form_data.tag_name, user_id) - chatIdTag = ChatIdTagModel(**{"user_id": user_id, "tag_name": tag.name}) + print(tag) + id = str(uuid.uuid4()) + chatIdTag = ChatIdTagModel( + **{ + "id": id, + "user_id": user_id, + "chat_id": form_data.chat_id, + "tag_name": tag.name, + "timestamp": int(time.time()), + } + ) try: result = ChatIdTag.create(**chatIdTag.model_dump()) if result: @@ -109,19 +124,17 @@ class TagTable: def get_tags_by_chat_id_and_user_id( self, chat_id: str, user_id: str ) -> List[TagModel]: + tag_names = [ + ChatIdTagModel(**model_to_dict(chat_id_tag)).tag_name + for chat_id_tag in ChatIdTag.select() + .where((ChatIdTag.user_id == user_id) & (ChatIdTag.chat_id == chat_id)) + .order_by(ChatIdTag.timestamp.desc()) + ] + + print(tag_names) return [ TagModel(**model_to_dict(tag)) - for tag in Tag.select().where( - Tag.name - in [ - ChatIdTagModel(**model_to_dict(chat_id_tag)).tag_name - for chat_id_tag in ChatIdTag.select() - .where( - (ChatIdTag.user_id == user_id) & (ChatIdTag.chat_id == chat_id) - ) - .order_by(ChatIdTag.timestamp.desc()) - ] - ) + for tag in Tag.select().where(Tag.name.in_(tag_names)) ] def get_chat_ids_by_tag_name_and_user_id( @@ -152,7 +165,8 @@ class TagTable: & (ChatIdTag.chat_id == chat_id) & (ChatIdTag.user_id == user_id) ) - query.execute() # Remove the rows, return number of rows removed. + res = query.execute() # Remove the rows, return number of rows removed. + print(res) tag_count = self.count_chat_ids_by_tag_name_and_user_id(tag_name, user_id) if tag_count == 0: @@ -163,7 +177,8 @@ class TagTable: query.execute() # Remove the rows, return number of rows removed. return True - except: + except Exception as e: + print("delete_tag", e) return False def delete_tags_by_chat_id_and_user_id(self, chat_id: str, user_id: str) -> bool: diff --git a/backend/apps/web/routers/chats.py b/backend/apps/web/routers/chats.py index 0c9aa573..38685826 100644 --- a/backend/apps/web/routers/chats.py +++ b/backend/apps/web/routers/chats.py @@ -19,6 +19,7 @@ from apps.web.models.chats import ( from apps.web.models.tags import ( TagModel, + ChatIdTagModel, ChatIdTagForm, ChatTagsResponse, Tags, @@ -132,7 +133,8 @@ async def delete_chat_by_id(id: str, user=Depends(get_current_user)): async def get_chat_tags_by_id(id: str, user=Depends(get_current_user)): tags = Tags.get_tags_by_chat_id_and_user_id(id, user.id) - if tags: + if tags != None: + print(tags) return tags else: raise HTTPException( @@ -145,17 +147,25 @@ async def get_chat_tags_by_id(id: str, user=Depends(get_current_user)): ############################ -@router.post("/{id}/tags", response_model=Optional[ChatTagsResponse]) +@router.post("/{id}/tags", response_model=Optional[ChatIdTagModel]) async def add_chat_tag_by_id( id: str, form_data: ChatIdTagForm, user=Depends(get_current_user) ): - tag = Tags.add_tag_to_chat(user.id, {"tag_name": form_data.tag_name, "chat_id": id}) + tags = Tags.get_tags_by_chat_id_and_user_id(id, user.id) - if tag: - return tag + if form_data.tag_name not in tags: + tag = Tags.add_tag_to_chat(user.id, form_data) + + if tag: + return tag + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=ERROR_MESSAGES.NOT_FOUND, + ) else: raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND + status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT() ) diff --git a/src/lib/components/layout/Navbar.svelte b/src/lib/components/layout/Navbar.svelte index 521e7abc..5899d582 100644 --- a/src/lib/components/layout/Navbar.svelte +++ b/src/lib/components/layout/Navbar.svelte @@ -12,22 +12,11 @@ export let title: string = 'Ollama Web UI'; export let shareEnabled: boolean = false; - let showShareChatModal = false; + export let tags = []; + export let addTag: Function; + export let deleteTag: Function; - let tags = [ - // { - // name: 'general' - // }, - // { - // name: 'medicine' - // }, - // { - // name: 'cooking' - // }, - // { - // name: 'education' - // } - ]; + let showShareChatModal = false; let tagName = ''; let showTagInput = false; @@ -74,16 +63,17 @@ saveAs(blob, `chat-${chat.title}.txt`); }; - const addTag = () => { - if (!tags.find((e) => e.name === tagName)) { - tags = [ - ...tags, - { - name: JSON.parse(JSON.stringify(tagName)) - } - ]; - } + const addTagHandler = () => { + // if (!tags.find((e) => e.name === tagName)) { + // tags = [ + // ...tags, + // { + // name: JSON.parse(JSON.stringify(tagName)) + // } + // ]; + // } + addTag(tagName); tagName = ''; showTagInput = false; }; @@ -126,48 +116,19 @@
-
- {#each tags as tag} -
-
- {tag.name} -
- -
- {/each} - -
- {#if showTagInput} -
- - +
+ {tag.name} +
+ {/each} - - {/if} - - -
-
- {#if shareEnabled} + +
+ + + {/if} + + +
+
+ + {#each $tags as tag} + + {/each} +
+ {/if} +
{#each $chats.filter((chat) => { if (search === '') { diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts index c7d8f5e6..7880235c 100644 --- a/src/lib/stores/index.ts +++ b/src/lib/stores/index.ts @@ -10,6 +10,7 @@ export const theme = writable('dark'); export const chatId = writable(''); export const chats = writable([]); +export const tags = writable([]); export const models = writable([]); export const modelfiles = writable([]); diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 39ae0eea..c7839d93 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -20,7 +20,8 @@ models, modelfiles, prompts, - documents + documents, + tags } from '$lib/stores'; import { REQUIRED_OLLAMA_VERSION, WEBUI_API_BASE_URL } from '$lib/constants'; @@ -29,6 +30,7 @@ import { checkVersion } from '$lib/utils'; import ShortcutsModal from '$lib/components/chat/ShortcutsModal.svelte'; import { getDocs } from '$lib/apis/documents'; + import { getAllChatTags } from '$lib/apis/chats'; let ollamaVersion = ''; let loaded = false; @@ -106,6 +108,7 @@ await modelfiles.set(await getModelfiles(localStorage.token)); await prompts.set(await getPrompts(localStorage.token)); await documents.set(await getDocs(localStorage.token)); + await tags.set(await getAllChatTags(localStorage.token)); modelfiles.subscribe(async () => { // should fetch models diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index 29e4f201..9507579c 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -6,7 +6,16 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; - import { models, modelfiles, user, settings, chats, chatId, config } from '$lib/stores'; + import { + models, + modelfiles, + user, + settings, + chats, + chatId, + config, + tags as _tags + } from '$lib/stores'; import { copyToClipboard, splitStream } from '$lib/utils'; import { generateChatCompletion, cancelChatCompletion, generateTitle } from '$lib/apis/ollama'; @@ -14,6 +23,7 @@ addTagById, createNewChat, deleteTagById, + getAllChatTags, getChatList, getTagsById, updateChatById @@ -695,6 +705,8 @@ chat = await updateChatById(localStorage.token, $chatId, { tags: tags }); + + _tags.set(await getAllChatTags(localStorage.token)); }; const deleteTag = async (tagName) => { @@ -704,6 +716,8 @@ chat = await updateChatById(localStorage.token, $chatId, { tags: tags }); + + _tags.set(await getAllChatTags(localStorage.token)); }; const setChatTitle = async (_chatId, _title) => { diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte index 37f6f39c..206b7398 100644 --- a/src/routes/(app)/c/[id]/+page.svelte +++ b/src/routes/(app)/c/[id]/+page.svelte @@ -6,7 +6,16 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; - import { models, modelfiles, user, settings, chats, chatId, config } from '$lib/stores'; + import { + models, + modelfiles, + user, + settings, + chats, + chatId, + config, + tags as _tags + } from '$lib/stores'; import { copyToClipboard, splitStream, convertMessagesToHistory } from '$lib/utils'; import { generateChatCompletion, generateTitle } from '$lib/apis/ollama'; @@ -14,6 +23,7 @@ addTagById, createNewChat, deleteTagById, + getAllChatTags, getChatById, getChatList, getTagsById, @@ -709,8 +719,10 @@ tags = await getTags(); chat = await updateChatById(localStorage.token, $chatId, { - tags: tags.map((tag) => tag.name) + tags: tags }); + + _tags.set(await getAllChatTags(localStorage.token)); }; const deleteTag = async (tagName) => { @@ -718,8 +730,10 @@ tags = await getTags(); chat = await updateChatById(localStorage.token, $chatId, { - tags: tags.map((tag) => tag.name) + tags: tags }); + + _tags.set(await getAllChatTags(localStorage.token)); }; onMount(async () => { From 76529acced2449b992f8a6a21d5f4f57287ec1ce Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Thu, 18 Jan 2024 02:57:31 -0800 Subject: [PATCH 17/29] doc: feature --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dfa7c1a5..3a14b00a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ Also check our sibling project, [OllamaHub](https://ollamahub.com/), where you c - 👍👎 **RLHF Annotation**: Empower your messages by rating them with thumbs up and thumbs down, facilitating the creation of datasets for Reinforcement Learning from Human Feedback (RLHF). Utilize your messages to train or fine-tune models, all while ensuring the confidentiality of locally saved data. +- 🏷️ **Conversation Tagging**: Effortlessly categorize and locate specific chats for quick reference and streamlined data collection. + - 📥🗑️ **Download/Delete Models**: Easily download or remove models directly from the web UI. - ⬆️ **GGUF File Model Creation**: Effortlessly create Ollama models by uploading GGUF files directly from the web UI. Streamlined process with options to upload from your machine or download GGUF files from Hugging Face. From 1a06b0cea6151804e54f782e7dc17ededcb3e94c Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Thu, 18 Jan 2024 16:38:47 -0800 Subject: [PATCH 18/29] fix: old chat log import issue --- src/lib/components/chat/SettingsModal.svelte | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/SettingsModal.svelte b/src/lib/components/chat/SettingsModal.svelte index c79fe106..bd80cd59 100644 --- a/src/lib/components/chat/SettingsModal.svelte +++ b/src/lib/components/chat/SettingsModal.svelte @@ -142,13 +142,20 @@ importChats(chats); }; - reader.readAsText(importFiles[0]); + if (importFiles.length > 0) { + reader.readAsText(importFiles[0]); + } } const importChats = async (_chats) => { for (const chat of _chats) { console.log(chat); - await createNewChat(localStorage.token, chat.chat); + + if (chat.chat) { + await createNewChat(localStorage.token, chat.chat); + } else { + await createNewChat(localStorage.token, chat); + } } await chats.set(await getChatList(localStorage.token)); From 4d85e2cb157f76bc61ecdfc2cdc42bf683ad4aa8 Mon Sep 17 00:00:00 2001 From: Brandon Hulston Date: Fri, 19 Jan 2024 11:22:28 -0700 Subject: [PATCH 19/29] Add validation for chatGPT imports, stopping any breaking issues when imports are corrupted/not compatible --- src/lib/utils/index.ts | 74 ++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index e7dfba2c..b12bdedd 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -206,25 +206,32 @@ const convertOpenAIMessages = (convo) => { const mapping = convo['mapping']; const messages = []; let currentId = ''; + let lastId = null; for (let message_id in mapping) { const message = mapping[message_id]; currentId = message_id; - if (message['message'] == null || message['message']['content']['parts'][0] == '') { - // Skip chat messages with no content - continue; - } else { - const new_chat = { - id: message_id, - parentId: messages.length > 0 && message['parent'] in mapping ? message['parent'] : null, - childrenIds: message['children'] || [], - role: message['message']?.['author']?.['role'] !== 'user' ? 'assistant' : 'user', - content: message['message']?.['content']?.['parts']?.[0] || '', - model: 'gpt-3.5-turbo', - done: true, - context: null - }; - messages.push(new_chat); + try { + if (messages.length == 0 && (message['message'] == null || + (message['message']['content']['parts']?.[0] == '' && message['message']['content']['text'] == null))) { + // Skip chat messages with no content + continue; + } else { + const new_chat = { + id: message_id, + parentId: lastId, + childrenIds: message['children'] || [], + role: message['message']?.['author']?.['role'] !== 'user' ? 'assistant' : 'user', + content: message['message']?.['content']?.['parts']?.[0] || message['message']?.['content']?.['text'] || '', + model: 'gpt-3.5-turbo', + done: true, + context: null + }; + messages.push(new_chat); + lastId = currentId; + } + } catch (error) { + console.log("Error with", message, "\nError:", error); } } @@ -245,13 +252,45 @@ const convertOpenAIMessages = (convo) => { return chat; }; +const validateChat = (chat) => { + // Because ChatGPT sometimes has features we can't use like DALL-E or migh have corrupted messages, need to validate + const messages = chat.messages; + + // Check if messages array is empty + if (messages.length === 0) { + return false; + } + + // Last message's children should be an empty array + const lastMessage = messages[messages.length - 1]; + if (lastMessage.childrenIds.length !== 0) { + return false; + } + + // First message's parent should be null + const firstMessage = messages[0]; + if (firstMessage.parentId !== null) { + return false; + } + + // Every message's content should be a string + for (let message of messages) { + if (typeof message.content !== 'string') { + return false; + } + } + + return true; +}; + export const convertOpenAIChats = (_chats) => { // Create a list of dictionaries with each conversation from import const chats = []; + let failed = 0; for (let convo of _chats) { const chat = convertOpenAIMessages(convo); - if (Object.keys(chat.history.messages).length > 0) { + if (validateChat(chat)) { chats.push({ id: convo['id'], user_id: '', @@ -259,7 +298,8 @@ export const convertOpenAIChats = (_chats) => { chat: chat, timestamp: convo['timestamp'] }); - } + } else { failed ++} } + console.log(failed, "Conversations could not be imported"); return chats; }; From 8662437a9f7d06b4fc6d22713e472428636f7562 Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Sat, 20 Jan 2024 04:17:06 +0530 Subject: [PATCH 20/29] Add workaround for gpt-4-vision-preview model --- backend/apps/openai/main.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/backend/apps/openai/main.py b/backend/apps/openai/main.py index ef9330c5..cbf3043a 100644 --- a/backend/apps/openai/main.py +++ b/backend/apps/openai/main.py @@ -84,9 +84,37 @@ async def proxy(path: str, request: Request, user=Depends(get_current_user)): raise HTTPException(status_code=401, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND) - body = await request.body() - # headers = dict(request.headers) - # print(headers) + # Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision) + try: + body_str = (await request.body()).decode('utf-8') + except UnicodeDecodeError as e: + print("Error decoding request body:", e) + raise HTTPException(status_code=400, detail="Invalid request body") + # Check if the body is not empty + if body_str: + try: + + body_dict = json.loads(body_str) + except json.JSONDecodeError as e: + print("Error loading request body into a dictionary:", e) + raise HTTPException(status_code=400, detail="Invalid JSON in request body") + + # Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 10000 + # This is a workaround until OpenAI fixes the issue with this model + if body_dict.get("model") == "gpt-4-vision-preview": + body_dict["max_tokens"] = 10000 + print("Modified body_dict:", body_dict) + + # Try to convert the modified body back to JSON + try: + # Convert the modified body back to JSON + body_json = json.dumps(body_dict) + except TypeError as e: + print("Error converting modified body to JSON:", e) + raise HTTPException(status_code=500, detail="Internal server error") + else: + body_json = body_str # If the body is empty, use it as is + headers = {} headers["Authorization"] = f"Bearer {app.state.OPENAI_API_KEY}" @@ -96,7 +124,7 @@ async def proxy(path: str, request: Request, user=Depends(get_current_user)): r = requests.request( method=request.method, url=target_url, - data=body, + data=body_json, headers=headers, stream=True, ) From 60afd6ecddf4eae0808f193a6c146eb378ba076b Mon Sep 17 00:00:00 2001 From: Aditya Pratap Singh Date: Sat, 20 Jan 2024 04:34:47 +0530 Subject: [PATCH 21/29] Add workaround for gpt-4-vision-preview model that support 4k tokens --- backend/apps/openai/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/apps/openai/main.py b/backend/apps/openai/main.py index cbf3043a..1544949e 100644 --- a/backend/apps/openai/main.py +++ b/backend/apps/openai/main.py @@ -99,10 +99,10 @@ async def proxy(path: str, request: Request, user=Depends(get_current_user)): print("Error loading request body into a dictionary:", e) raise HTTPException(status_code=400, detail="Invalid JSON in request body") - # Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 10000 + # Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000 # This is a workaround until OpenAI fixes the issue with this model if body_dict.get("model") == "gpt-4-vision-preview": - body_dict["max_tokens"] = 10000 + body_dict["max_tokens"] = 4000 print("Modified body_dict:", body_dict) # Try to convert the modified body back to JSON From 6a63c94153dd76f41857019530ef0054913b5cdd Mon Sep 17 00:00:00 2001 From: Shiyinq Date: Sat, 20 Jan 2024 21:54:53 +0700 Subject: [PATCH 22/29] feat: add guard clause to improve signup process --- backend/apps/web/routers/auths.py | 66 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/backend/apps/web/routers/auths.py b/backend/apps/web/routers/auths.py index f245601d..a0772223 100644 --- a/backend/apps/web/routers/auths.py +++ b/backend/apps/web/routers/auths.py @@ -91,42 +91,40 @@ async def signin(form_data: SigninForm): @router.post("/signup", response_model=SigninResponse) async def signup(request: Request, form_data: SignupForm): - if request.app.state.ENABLE_SIGNUP: - if validate_email_format(form_data.email.lower()): - if not Users.get_user_by_email(form_data.email.lower()): - try: - role = "admin" if Users.get_num_users() == 0 else "pending" - hashed = get_password_hash(form_data.password) - user = Auths.insert_new_auth(form_data.email.lower(), - hashed, form_data.name, role) - - if user: - token = create_token(data={"email": user.email}) - # response.set_cookie(key='token', value=token, httponly=True) - - return { - "token": token, - "token_type": "Bearer", - "id": user.id, - "email": user.email, - "name": user.name, - "role": user.role, - "profile_image_url": user.profile_image_url, - } - else: - raise HTTPException( - 500, detail=ERROR_MESSAGES.CREATE_USER_ERROR) - except Exception as err: - raise HTTPException(500, - detail=ERROR_MESSAGES.DEFAULT(err)) - else: - raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN) - else: - raise HTTPException(400, - detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT) - else: + if not request.app.state.ENABLE_SIGNUP: raise HTTPException(400, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) + + if not validate_email_format(form_data.email.lower()): + raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT) + + if Users.get_user_by_email(form_data.email.lower()): + raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN) + + try: + role = "admin" if Users.get_num_users() == 0 else "pending" + hashed = get_password_hash(form_data.password) + user = Auths.insert_new_auth(form_data.email.lower(), + hashed, form_data.name, role) + if user: + token = create_token(data={"email": user.email}) + # response.set_cookie(key='token', value=token, httponly=True) + + return { + "token": token, + "token_type": "Bearer", + "id": user.id, + "email": user.email, + "name": user.name, + "role": user.role, + "profile_image_url": user.profile_image_url, + } + else: + raise HTTPException( + 500, detail=ERROR_MESSAGES.CREATE_USER_ERROR) + except Exception as err: + raise HTTPException(500, + detail=ERROR_MESSAGES.DEFAULT(err)) ############################ # ToggleSignUp From b26e0fb7e707e202b42a8e3e5fc5067c26968f00 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Mon, 22 Jan 2024 01:37:54 -0800 Subject: [PATCH 23/29] refac --- backend/apps/openai/main.py | 70 +++++++++++++------------------------ 1 file changed, 25 insertions(+), 45 deletions(-) diff --git a/backend/apps/openai/main.py b/backend/apps/openai/main.py index 1544949e..fd6acee7 100644 --- a/backend/apps/openai/main.py +++ b/backend/apps/openai/main.py @@ -37,19 +37,16 @@ async def get_openai_url(user=Depends(get_current_user)): if user and user.role == "admin": return {"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL} else: - raise HTTPException(status_code=401, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED) + raise HTTPException(status_code=401, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) @app.post("/url/update") -async def update_openai_url(form_data: UrlUpdateForm, - user=Depends(get_current_user)): +async def update_openai_url(form_data: UrlUpdateForm, user=Depends(get_current_user)): if user and user.role == "admin": app.state.OPENAI_API_BASE_URL = form_data.url return {"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL} else: - raise HTTPException(status_code=401, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED) + raise HTTPException(status_code=401, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) @app.get("/key") @@ -57,19 +54,16 @@ async def get_openai_key(user=Depends(get_current_user)): if user and user.role == "admin": return {"OPENAI_API_KEY": app.state.OPENAI_API_KEY} else: - raise HTTPException(status_code=401, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED) + raise HTTPException(status_code=401, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) @app.post("/key/update") -async def update_openai_key(form_data: KeyUpdateForm, - user=Depends(get_current_user)): +async def update_openai_key(form_data: KeyUpdateForm, user=Depends(get_current_user)): if user and user.role == "admin": app.state.OPENAI_API_KEY = form_data.key return {"OPENAI_API_KEY": app.state.OPENAI_API_KEY} else: - raise HTTPException(status_code=401, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED) + raise HTTPException(status_code=401, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) @@ -78,43 +72,29 @@ async def proxy(path: str, request: Request, user=Depends(get_current_user)): print(target_url, app.state.OPENAI_API_KEY) if user.role not in ["user", "admin"]: - raise HTTPException(status_code=401, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED) + raise HTTPException(status_code=401, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) if app.state.OPENAI_API_KEY == "": - raise HTTPException(status_code=401, - detail=ERROR_MESSAGES.API_KEY_NOT_FOUND) + raise HTTPException(status_code=401, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND) + body = await request.body() + + # TODO: Remove below after gpt-4-vision fix from Open AI # Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision) try: - body_str = (await request.body()).decode('utf-8') - except UnicodeDecodeError as e: - print("Error decoding request body:", e) - raise HTTPException(status_code=400, detail="Invalid request body") - # Check if the body is not empty - if body_str: - try: - - body_dict = json.loads(body_str) - except json.JSONDecodeError as e: - print("Error loading request body into a dictionary:", e) - raise HTTPException(status_code=400, detail="Invalid JSON in request body") - + body = body.decode("utf-8") + body = json.loads(body) + # Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000 # This is a workaround until OpenAI fixes the issue with this model - if body_dict.get("model") == "gpt-4-vision-preview": - body_dict["max_tokens"] = 4000 - print("Modified body_dict:", body_dict) - - # Try to convert the modified body back to JSON - try: - # Convert the modified body back to JSON - body_json = json.dumps(body_dict) - except TypeError as e: - print("Error converting modified body to JSON:", e) - raise HTTPException(status_code=500, detail="Internal server error") - else: - body_json = body_str # If the body is empty, use it as is + if body.get("model") == "gpt-4-vision-preview": + body["max_tokens"] = 4000 + print("Modified body_dict:", body) + # Convert the modified body back to JSON + body = json.dumps(body) + except json.JSONDecodeError as e: + print("Error loading request body into a dictionary:", e) + raise HTTPException(status_code=400, detail="Invalid JSON in request body") headers = {} headers["Authorization"] = f"Bearer {app.state.OPENAI_API_KEY}" @@ -124,7 +104,7 @@ async def proxy(path: str, request: Request, user=Depends(get_current_user)): r = requests.request( method=request.method, url=target_url, - data=body_json, + data=body, headers=headers, stream=True, ) @@ -153,8 +133,8 @@ async def proxy(path: str, request: Request, user=Depends(get_current_user)): if "openai" in app.state.OPENAI_API_BASE_URL and path == "models": response_data["data"] = list( - filter(lambda model: "gpt" in model["id"], - response_data["data"])) + filter(lambda model: "gpt" in model["id"], response_data["data"]) + ) return response_data except Exception as e: From 83181b7968b462ad5e218802273213b6fa6ca3ab Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Mon, 22 Jan 2024 01:41:00 -0800 Subject: [PATCH 24/29] fix: add max_token only when field not present --- backend/apps/openai/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/apps/openai/main.py b/backend/apps/openai/main.py index fd6acee7..1529ab93 100644 --- a/backend/apps/openai/main.py +++ b/backend/apps/openai/main.py @@ -87,7 +87,8 @@ async def proxy(path: str, request: Request, user=Depends(get_current_user)): # Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000 # This is a workaround until OpenAI fixes the issue with this model if body.get("model") == "gpt-4-vision-preview": - body["max_tokens"] = 4000 + if "max_tokens" not in body: + body["max_tokens"] = 4000 print("Modified body_dict:", body) # Convert the modified body back to JSON From e758855590524cad590b415d19658cd0168a5e9e Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Mon, 22 Jan 2024 03:33:49 -0800 Subject: [PATCH 25/29] refac: response message --- .../components/chat/Messages/CodeBlock.svelte | 36 ++++++ .../chat/Messages/ResponseMessage.svelte | 103 ++++++------------ 2 files changed, 67 insertions(+), 72 deletions(-) create mode 100644 src/lib/components/chat/Messages/CodeBlock.svelte diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte new file mode 100644 index 00000000..a8df6bfa --- /dev/null +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -0,0 +1,36 @@ + + +
+
+
{lang}
+ +
+ +
{@html highlightedCode || code}
+
diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 37a593f1..21dafed5 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -1,17 +1,16 @@ -
+
@@ -30,7 +30,7 @@ >
-
{@html highlightedCode || code}
From 917ab08f5c001ad9d3fe007cae9876ac92f1738d Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Mon, 22 Jan 2024 03:43:48 -0800 Subject: [PATCH 27/29] fix: code block styling --- src/lib/components/chat/Messages/CodeBlock.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index df50104e..c93485c4 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -22,7 +22,7 @@
{lang}
+
-
+
{@html lang}
+ +
-
{@html highlightedCode || code}
-
+
{@html highlightedCode || code}
+
+{/if} diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 21dafed5..ef88207f 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -154,328 +154,164 @@ }); -
- +{#key message.id} +
+ -
- - {#if message.model in modelfiles} - {modelfiles[message.model]?.title} +
+ + {#if message.model in modelfiles} + {modelfiles[message.model]?.title} + {:else} + Ollama {message.model ? ` ${message.model}` : ''} + {/if} + + {#if message.timestamp} + + {/if} + + + {#if message.content === ''} + {:else} - Ollama {message.model ? ` ${message.model}` : ''} - {/if} - - {#if message.timestamp} - - {/if} - - - {#if message.content === ''} - - {:else} -
-
- {#if edit === true} -
-