Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Vision API #500

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ShareGPTSubmitBodyInterface } from '@type/api';
import { ConfigInterface, MessageInterface, ModelOptions } from '@type/chat';
import { ConfigInterface, ImageContentInterface, MessageInterface, ModelOptions } from '@type/chat';
import { isAzureEndpoint } from '@utils/api';



export const getChatCompletion = async (
endpoint: string,
messages: MessageInterface[],
Expand Down Expand Up @@ -102,7 +104,7 @@ export const getChatCompletionStream = async (
body: JSON.stringify({
messages,
...config,
max_tokens: undefined,
max_tokens: config.max_tokens,
stream: true,
}),
});
Expand All @@ -112,7 +114,7 @@ export const getChatCompletionStream = async (
if (text.includes('model_not_found')) {
throw new Error(
text +
'\nMessage from Better ChatGPT:\nPlease ensure that you have access to the GPT-4 API!'
'\nMessage from Better ChatGPT:\nPlease ensure that you have access to the GPT-4 API!'
);
} else {
throw new Error(
Expand Down
6 changes: 5 additions & 1 deletion src/components/Chat/ChatContent/ChatContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import useSubmit from '@hooks/useSubmit';
import DownloadChat from './DownloadChat';
import CloneChat from './CloneChat';
import ShareGPT from '@components/ShareGPT';
import { ImageContentInterface, TextContentInterface } from '@type/chat';

const ChatContent = () => {
const inputRole = useStore((state) => state.inputRole);
Expand Down Expand Up @@ -79,7 +80,10 @@ const ChatContent = () => {

<Message
role={inputRole}
content=''
// For now we always initizlize a new message with an empty text content.
// It is possible to send a message to the API without a TextContentInterface,
// but the UI would need to be modified to allow the user to control the order of text and image content
content={[{type: 'text', text: ''} as TextContentInterface]}
messageIndex={stickyIndex}
sticky
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import { matchSorter } from 'match-sorter';
import { Prompt } from '@type/prompt';

import useHideOnOutsideClick from '@hooks/useHideOnOutsideClick';
import { ContentInterface } from '@type/chat';

const CommandPrompt = ({
_setContent,
}: {
_setContent: React.Dispatch<React.SetStateAction<string>>;
_setContent: React.Dispatch<React.SetStateAction<ContentInterface[]>>;
}) => {
const { t } = useTranslation();
const prompts = useStore((state) => state.prompts);
Expand Down Expand Up @@ -69,7 +70,7 @@ const CommandPrompt = ({
<li
className='px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white cursor-pointer text-start w-full'
onClick={() => {
_setContent((prev) => prev + cp.prompt);
_setContent((prev) => [{type: 'text', text: prev + cp.prompt}, ...prev.slice(1)]);
setDropDown(false);
}}
key={cp.id}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Chat/ChatContent/Message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import useStore from '@store/store';
import Avatar from './Avatar';
import MessageContent from './MessageContent';

import { Role } from '@type/chat';
import { ContentInterface, Role } from '@type/chat';
import RoleSelector from './RoleSelector';

// const backgroundStyle: { [role in Role]: string } = {
Expand All @@ -22,7 +22,7 @@ const Message = React.memo(
sticky = false,
}: {
role: Role;
content: string;
content: ContentInterface[],
messageIndex: number;
sticky?: boolean;
}) => {
Expand Down
3 changes: 2 additions & 1 deletion src/components/Chat/ChatContent/Message/MessageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useStore from '@store/store';

import ContentView from './View/ContentView';
import EditView from './View/EditView';
import { ContentInterface } from '@type/chat';

const MessageContent = ({
role,
Expand All @@ -11,7 +12,7 @@ const MessageContent = ({
sticky = false,
}: {
role: string;
content: string;
content: ContentInterface[];
messageIndex: number;
sticky?: boolean;
}) => {
Expand Down
17 changes: 12 additions & 5 deletions src/components/Chat/ChatContent/Message/View/ContentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import CrossIcon from '@icon/CrossIcon';

import useSubmit from '@hooks/useSubmit';

import { ChatInterface } from '@type/chat';
import { ChatInterface, ContentInterface, ImageContentInterface, TextContentInterface } from '@type/chat';

import { codeLanguageSubset } from '@constants/chat';

Expand All @@ -41,7 +41,7 @@ const ContentView = memo(
messageIndex,
}: {
role: string;
content: string;
content: ContentInterface[],
setIsEdit: React.Dispatch<React.SetStateAction<boolean>>;
messageIndex: number;
}) => {
Expand Down Expand Up @@ -100,7 +100,7 @@ const ContentView = memo(
};

const handleCopy = () => {
navigator.clipboard.writeText(content);
navigator.clipboard.writeText((content[0] as TextContentInterface).text);
};

return (
Expand Down Expand Up @@ -129,12 +129,19 @@ const ContentView = memo(
p,
}}
>
{content}
{(content[0] as TextContentInterface).text}
</ReactMarkdown>
) : (
<span className='whitespace-pre-wrap'>{content}</span>
<span className='whitespace-pre-wrap'>{(content[0] as TextContentInterface).text}</span>
)}
</div>
<div className="flex gap-4">
{(content.slice(1) as ImageContentInterface[]).map((image, index) => (
<div key={index} className="image-container">
<img src={image.image_url.url} alt={`uploaded-${index}`} className="h-20" />
</div>
))}
</div>
<div className='flex justify-end gap-2 w-full mt-2'>
{isDelete || (
<>
Expand Down
Loading