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

fix: various UI bugs #18

Merged
merged 4 commits into from
Jul 9, 2024
Merged
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
2 changes: 1 addition & 1 deletion webapp/app/_components/assistant-thread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useThreadRuns } from '@/lib/use-thread-runs';


interface Props {
threadId?: string
threadId: string
}

export default function AssistantThread({ threadId }: Props) {
Expand Down
13 changes: 3 additions & 10 deletions webapp/app/_components/create-new-thread.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
'use client';
import { NewThread } from '@/components/new-thread';
import { useCreateThread } from '@/lib/use-create-thread';
import { useRouter } from 'next/navigation';
import { useCallback } from 'react';


export function CreateNewThread() {
const router = useRouter();
const createThread = useCreateThread();

const onClick = useCallback(async () => {
const newThread = await createThread();
router.push(`/thread/${newThread.id}`);
}, [createThread, router]);
export function CreateNewThread() {
const createThread = useCreateThread({ redirect: true });

return (
<NewThread onClick={onClick} />
<NewThread onClick={createThread} />
);
}
2 changes: 0 additions & 2 deletions webapp/app/_components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import BrandLink from './brand.link';
import SettingLink from './settings.link';
import PrivacyToggle from './privacy-toggle';
import StatusIndicator from './status-indicator';


Expand All @@ -13,7 +12,6 @@ export default function Header() {
</div>
<div className='flex items-center gap-4'>
<StatusIndicator />
<PrivacyToggle />
<SettingLink />
</div>
</div>
Expand Down
26 changes: 26 additions & 0 deletions webapp/app/_components/new-thread-prompt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use client';
import Chat from '@/components/chat';
import { useCreateThread } from '@/lib/use-create-thread';
import { useCurrentModel } from '@/lib/use-current-model';
import { useState } from 'react';


export default function NewThreadPrompt() {
const { data: model } = useCurrentModel();
const [input, setInput] = useState('');
const createThread = useCreateThread({ redirect: true });

return (
<Chat
messages={[]}
input={input}
onChange={(event) => setInput(event.target.value)}
onSubmit={(event) => {
event.preventDefault();
event.stopPropagation();
createThread(input);
}}
details={<span className='text-slate-400 text-xs'>{model}</span>}
/>
);
}
4 changes: 2 additions & 2 deletions webapp/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import AssistantThread from './_components/assistant-thread';
import NewThreadPrompt from './_components/new-thread-prompt';
import RecentThreads from './_components/recent-threads';


export default function Home() {
return (
<main className="h-full flex flex-col">
<RecentThreads />
<AssistantThread />
<NewThreadPrompt />
</main>
);
}
2 changes: 1 addition & 1 deletion webapp/components/threads-preview-collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface Props<TThread extends ThreadPreviewComponentDto = ThreadPreviewCompone

export function ThreadsPreviewCollection<TThread extends ThreadPreviewComponentDto = ThreadPreviewComponentDto>({ error, threads, isLoading, className, children, onDelete }: Props<TThread>) {
return (
<div className={cn('w-full flex flex-col items-center gap-1 overflow-y-auto', className)}>
<div className={cn('w-full flex flex-col items-center gap-1 overflow-y-auto pb-4', className)}>
<div className='w-full flex justify-start gap-4'>
{children}
{ threads?.map((thread) => (
Expand Down
31 changes: 25 additions & 6 deletions webapp/lib/use-create-thread.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
import { useCallback } from 'react';
import { useRouter } from 'next/navigation';
import { useOpenaiClient } from './openai-client';
import { useListThreads } from './use-list-threads';
import { Thread } from 'openai/resources/beta/threads/threads.mjs';
import { Thread, ThreadCreateParams } from 'openai/resources/beta/threads/threads.mjs';


export function useCreateThread(): () => Promise<Thread> {
interface Props {
redirect?: boolean
}

type CreateNewThread = (messageContent?: string) => Promise<Thread>;
export function useCreateThread({ redirect }: Props = {}): CreateNewThread {
const openai = useOpenaiClient();
const router = useRouter();
const { revalidate } = useListThreads();

return useCallback(async () => {
const newThread = await openai.beta.threads.create();
revalidate();
return useCallback<CreateNewThread>(async (messageContent) => {
const messages: ThreadCreateParams.Message[] = [];

if (messageContent) {
messages.push({ role: 'user', content: messageContent });
}
const newThread = await openai.beta.threads.create({
messages,
});

if (redirect) {
router.push(`/thread/${newThread.id}`);
} else {
revalidate();
}

return newThread;
}, [openai.beta.threads, revalidate]);
}, [openai.beta.threads, revalidate, redirect, router]);
}
72 changes: 40 additions & 32 deletions webapp/lib/use-openai-assistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ type MessageDelta = OpenAI.Beta.Threads.Messages.MessageDelta;

interface Props {
assistantId?: string;
threadId?: string;
threadId: string;
model?: string;
temperature?: number;
initialInput?: string;
}
export function useOpenAiAssistant({ assistantId = '', threadId: argsThreadId, model = 'openai:gpt-3.5-turbo', temperature }: Props = {}) {
const [messages, setMessages] = useState<Message[]>([ ]);
const [input, setInput] = useState('');
const [threadId, setThreadId] = useState<string | undefined>(argsThreadId);
export function useOpenAiAssistant({ assistantId = '', threadId, model = 'openai:gpt-3.5-turbo', temperature, initialInput }: Props) {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState(initialInput ?? '');
const [status, setStatus] = useState<AssistantStatus>('awaiting_message');
const [error, setError] = useState<undefined | Error>(undefined);
const streamRef = useRef<AssistantStream | null>(null);
Expand All @@ -34,20 +34,17 @@ export function useOpenAiAssistant({ assistantId = '', threadId: argsThreadId, m
}, []);

useEffect(() => {
setThreadId(argsThreadId);
if (!argsThreadId) return;

const fetchMessages = async () => {
try {
const newMessages = await openai.beta.threads.messages.list(argsThreadId);
const newMessages = await openai.beta.threads.messages.list(threadId);
setMessages(newMessages.data);
} catch (e) {
setUnknownError(e);
}
};
fetchMessages();

}, [openai.beta.threads.messages, argsThreadId, setUnknownError]);
}, [openai.beta.threads.messages, threadId, setUnknownError]);

const handleInputChange = (
event:
Expand All @@ -57,33 +54,15 @@ export function useOpenAiAssistant({ assistantId = '', threadId: argsThreadId, m
setInput(event.target.value);
};

const append = async (
message: CreateMessage,
) => {
const streamRun = useCallback(async () => {
try {
setStatus('in_progress');

let local_threadId = threadId;
if (!local_threadId) {
const thread = await openai.beta.threads.create();
local_threadId = thread.id;
setThreadId(local_threadId);
}

const created_message = await openai.beta.threads.messages.create(
local_threadId,
message
);
setMessages(messages => [
...messages,
created_message,
]);

abortControlerRef.current = new AbortController();
const signal = abortControlerRef.current.signal;

await new Promise<void>((resolve, rejects) => {
streamRef.current = openai.beta.threads.runs.stream(local_threadId, {
streamRef.current = openai.beta.threads.runs.stream(threadId, {
model,
assistant_id: assistantId,
temperature,
Expand Down Expand Up @@ -115,11 +94,39 @@ export function useOpenAiAssistant({ assistantId = '', threadId: argsThreadId, m
});
}
finally {
setInput('');
streamRef.current = null;
setStatus('awaiting_message');
}
};
}, [assistantId, messages, model, openai.beta.threads.runs, setUnknownError, temperature, threadId]);

useEffect(() => {
if (messages.at(-1)?.role === 'user') {
streamRun();
}
}, [messages, streamRun]);


const append = useCallback(async (
message?: CreateMessage,
) => {
setInput('');

try {
if (message) {
const created_message = await openai.beta.threads.messages.create(
threadId,
message
);
setMessages(messages => [
...messages,
created_message,
]);
}
} catch (e) {
setUnknownError(e);
}

}, [openai.beta.threads.messages, threadId, setUnknownError]);

const abort = useCallback(() => {
if (abortControlerRef.current) {
Expand All @@ -128,6 +135,7 @@ export function useOpenAiAssistant({ assistantId = '', threadId: argsThreadId, m
}
}, []);


const submitMessage = async (
event?: React.FormEvent<HTMLFormElement>,
) => {
Expand Down
5 changes: 3 additions & 2 deletions webapp/lib/use-openai-assistant.ui.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ import { useOpenaiClient } from './openai-client';


jest.mock('./openai-client');
describe('new-thread', () => {
describe('new-conversation', () => {
const fetch = jest.fn();
const TestComponent = () => {
const threadId = 'thread_abc123';
when(useOpenaiClient).mockReturnValue(new OpenAI({
apiKey: 'abc',
fetch,
dangerouslyAllowBrowser: true,
}));

const { status, messages, error, append } = useOpenAiAssistant();
const { status, messages, error, append } = useOpenAiAssistant({ threadId });

return (
<div>
Expand Down
1 change: 1 addition & 0 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"react-dom": "^18",
"react-markdown": "^9.0.1",
"react-syntax-highlighter": "^15.5.0",
"react-use": "^17.5.0",
"remark-gfm": "^4.0.0",
"swr": "^2.2.5",
"tailwind-merge": "^2.3.0",
Expand Down
5 changes: 3 additions & 2 deletions webapp/pages/thread/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import RecentThreads from '@/app/_components/recent-threads';

export default function Page() {
const router = useRouter();
const { id } = router.query;

const id = typeof router.query.id === 'string' ? router.query.id : undefined;

return (
<MainLayout>
<main className="h-full flex flex-col">
<RecentThreads />
{ id && typeof id === 'string' && <AssistantThread threadId={id} /> }
{ id && <AssistantThread threadId={id} /> }
</main>
</MainLayout>
);
Expand Down
Loading