fix: support setting visibility on initial chat creation (#975)
This commit is contained in:
parent
a3221fbcdc
commit
575c12503c
15 changed files with 158 additions and 32 deletions
|
|
@ -49,7 +49,8 @@ export async function POST(request: Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { id, message, selectedChatModel } = requestBody;
|
const { id, message, selectedChatModel, selectedVisibilityType } =
|
||||||
|
requestBody;
|
||||||
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
|
|
@ -80,7 +81,12 @@ export async function POST(request: Request) {
|
||||||
message,
|
message,
|
||||||
});
|
});
|
||||||
|
|
||||||
await saveChat({ id, userId: session.user.id, title });
|
await saveChat({
|
||||||
|
id,
|
||||||
|
userId: session.user.id,
|
||||||
|
title,
|
||||||
|
visibility: selectedVisibilityType,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
if (chat.userId !== session.user.id) {
|
if (chat.userId !== session.user.id) {
|
||||||
return new Response('Forbidden', { status: 403 });
|
return new Response('Forbidden', { status: 403 });
|
||||||
|
|
@ -236,7 +242,7 @@ export async function GET(request: Request) {
|
||||||
return new Response('Not found', { status: 404 });
|
return new Response('Not found', { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chat.userId !== session.user.id) {
|
if (chat.visibility === 'private' && chat.userId !== session.user.id) {
|
||||||
return new Response('Forbidden', { status: 403 });
|
return new Response('Forbidden', { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export const postRequestBodySchema = z.object({
|
||||||
.optional(),
|
.optional(),
|
||||||
}),
|
}),
|
||||||
selectedChatModel: z.enum(['chat-model', 'chat-model-reasoning']),
|
selectedChatModel: z.enum(['chat-model', 'chat-model-reasoning']),
|
||||||
|
selectedVisibilityType: z.enum(['public', 'private']),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type PostRequestBody = z.infer<typeof postRequestBodySchema>;
|
export type PostRequestBody = z.infer<typeof postRequestBodySchema>;
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ export default async function Page(props: { params: Promise<{ id: string }> }) {
|
||||||
<Chat
|
<Chat
|
||||||
id={chat.id}
|
id={chat.id}
|
||||||
initialMessages={convertToUIMessages(messagesFromDb)}
|
initialMessages={convertToUIMessages(messagesFromDb)}
|
||||||
selectedChatModel={DEFAULT_CHAT_MODEL}
|
initialChatModel={DEFAULT_CHAT_MODEL}
|
||||||
selectedVisibilityType={chat.visibility}
|
initialVisibilityType={chat.visibility}
|
||||||
isReadonly={session?.user?.id !== chat.userId}
|
isReadonly={session?.user?.id !== chat.userId}
|
||||||
session={session}
|
session={session}
|
||||||
autoResume={true}
|
autoResume={true}
|
||||||
|
|
@ -76,8 +76,8 @@ export default async function Page(props: { params: Promise<{ id: string }> }) {
|
||||||
<Chat
|
<Chat
|
||||||
id={chat.id}
|
id={chat.id}
|
||||||
initialMessages={convertToUIMessages(messagesFromDb)}
|
initialMessages={convertToUIMessages(messagesFromDb)}
|
||||||
selectedChatModel={chatModelFromCookie.value}
|
initialChatModel={chatModelFromCookie.value}
|
||||||
selectedVisibilityType={chat.visibility}
|
initialVisibilityType={chat.visibility}
|
||||||
isReadonly={session?.user?.id !== chat.userId}
|
isReadonly={session?.user?.id !== chat.userId}
|
||||||
session={session}
|
session={session}
|
||||||
autoResume={true}
|
autoResume={true}
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ export default async function Page() {
|
||||||
key={id}
|
key={id}
|
||||||
id={id}
|
id={id}
|
||||||
initialMessages={[]}
|
initialMessages={[]}
|
||||||
selectedChatModel={DEFAULT_CHAT_MODEL}
|
initialChatModel={DEFAULT_CHAT_MODEL}
|
||||||
selectedVisibilityType="private"
|
initialVisibilityType="private"
|
||||||
isReadonly={false}
|
isReadonly={false}
|
||||||
session={session}
|
session={session}
|
||||||
autoResume={false}
|
autoResume={false}
|
||||||
|
|
@ -43,8 +43,8 @@ export default async function Page() {
|
||||||
key={id}
|
key={id}
|
||||||
id={id}
|
id={id}
|
||||||
initialMessages={[]}
|
initialMessages={[]}
|
||||||
selectedChatModel={modelIdFromCookie.value}
|
initialChatModel={modelIdFromCookie.value}
|
||||||
selectedVisibilityType="private"
|
initialVisibilityType="private"
|
||||||
isReadonly={false}
|
isReadonly={false}
|
||||||
session={session}
|
session={session}
|
||||||
autoResume={false}
|
autoResume={false}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import { sheetArtifact } from '@/artifacts/sheet/client';
|
||||||
import { textArtifact } from '@/artifacts/text/client';
|
import { textArtifact } from '@/artifacts/text/client';
|
||||||
import equal from 'fast-deep-equal';
|
import equal from 'fast-deep-equal';
|
||||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||||
|
import type { VisibilityType } from './visibility-selector';
|
||||||
|
|
||||||
export const artifactDefinitions = [
|
export const artifactDefinitions = [
|
||||||
textArtifact,
|
textArtifact,
|
||||||
|
|
@ -66,6 +67,7 @@ function PureArtifact({
|
||||||
reload,
|
reload,
|
||||||
votes,
|
votes,
|
||||||
isReadonly,
|
isReadonly,
|
||||||
|
selectedVisibilityType,
|
||||||
}: {
|
}: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
input: string;
|
input: string;
|
||||||
|
|
@ -81,6 +83,7 @@ function PureArtifact({
|
||||||
handleSubmit: UseChatHelpers['handleSubmit'];
|
handleSubmit: UseChatHelpers['handleSubmit'];
|
||||||
reload: UseChatHelpers['reload'];
|
reload: UseChatHelpers['reload'];
|
||||||
isReadonly: boolean;
|
isReadonly: boolean;
|
||||||
|
selectedVisibilityType: VisibilityType;
|
||||||
}) {
|
}) {
|
||||||
const { artifact, setArtifact, metadata, setMetadata } = useArtifact();
|
const { artifact, setArtifact, metadata, setMetadata } = useArtifact();
|
||||||
|
|
||||||
|
|
@ -335,6 +338,7 @@ function PureArtifact({
|
||||||
append={append}
|
append={append}
|
||||||
className="bg-background dark:bg-muted"
|
className="bg-background dark:bg-muted"
|
||||||
setMessages={setMessages}
|
setMessages={setMessages}
|
||||||
|
selectedVisibilityType={selectedVisibilityType}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -503,6 +507,8 @@ export const Artifact = memo(PureArtifact, (prevProps, nextProps) => {
|
||||||
if (!equal(prevProps.votes, nextProps.votes)) return false;
|
if (!equal(prevProps.votes, nextProps.votes)) return false;
|
||||||
if (prevProps.input !== nextProps.input) return false;
|
if (prevProps.input !== nextProps.input) return false;
|
||||||
if (!equal(prevProps.messages, nextProps.messages.length)) return false;
|
if (!equal(prevProps.messages, nextProps.messages.length)) return false;
|
||||||
|
if (prevProps.selectedVisibilityType !== nextProps.selectedVisibilityType)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,26 +17,32 @@ import { getChatHistoryPaginationKey } from './sidebar-history';
|
||||||
import { toast } from './toast';
|
import { toast } from './toast';
|
||||||
import type { Session } from 'next-auth';
|
import type { Session } from 'next-auth';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
import { useChatVisibility } from '@/hooks/use-chat-visibility';
|
||||||
|
|
||||||
export function Chat({
|
export function Chat({
|
||||||
id,
|
id,
|
||||||
initialMessages,
|
initialMessages,
|
||||||
selectedChatModel,
|
initialChatModel,
|
||||||
selectedVisibilityType,
|
initialVisibilityType,
|
||||||
isReadonly,
|
isReadonly,
|
||||||
session,
|
session,
|
||||||
autoResume,
|
autoResume,
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
initialMessages: Array<UIMessage>;
|
initialMessages: Array<UIMessage>;
|
||||||
selectedChatModel: string;
|
initialChatModel: string;
|
||||||
selectedVisibilityType: VisibilityType;
|
initialVisibilityType: VisibilityType;
|
||||||
isReadonly: boolean;
|
isReadonly: boolean;
|
||||||
session: Session;
|
session: Session;
|
||||||
autoResume: boolean;
|
autoResume: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
|
|
||||||
|
const { visibilityType } = useChatVisibility({
|
||||||
|
chatId: id,
|
||||||
|
initialVisibilityType,
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
messages,
|
messages,
|
||||||
setMessages,
|
setMessages,
|
||||||
|
|
@ -57,7 +63,8 @@ export function Chat({
|
||||||
experimental_prepareRequestBody: (body) => ({
|
experimental_prepareRequestBody: (body) => ({
|
||||||
id,
|
id,
|
||||||
message: body.messages.at(-1),
|
message: body.messages.at(-1),
|
||||||
selectedChatModel,
|
selectedChatModel: initialChatModel,
|
||||||
|
selectedVisibilityType: visibilityType,
|
||||||
}),
|
}),
|
||||||
onFinish: () => {
|
onFinish: () => {
|
||||||
mutate(unstable_serialize(getChatHistoryPaginationKey));
|
mutate(unstable_serialize(getChatHistoryPaginationKey));
|
||||||
|
|
@ -109,8 +116,8 @@ export function Chat({
|
||||||
<div className="flex flex-col min-w-0 h-dvh bg-background">
|
<div className="flex flex-col min-w-0 h-dvh bg-background">
|
||||||
<ChatHeader
|
<ChatHeader
|
||||||
chatId={id}
|
chatId={id}
|
||||||
selectedModelId={selectedChatModel}
|
selectedModelId={initialChatModel}
|
||||||
selectedVisibilityType={selectedVisibilityType}
|
selectedVisibilityType={initialVisibilityType}
|
||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
session={session}
|
session={session}
|
||||||
/>
|
/>
|
||||||
|
|
@ -140,6 +147,7 @@ export function Chat({
|
||||||
messages={messages}
|
messages={messages}
|
||||||
setMessages={setMessages}
|
setMessages={setMessages}
|
||||||
append={append}
|
append={append}
|
||||||
|
selectedVisibilityType={visibilityType}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -160,6 +168,7 @@ export function Chat({
|
||||||
reload={reload}
|
reload={reload}
|
||||||
votes={votes}
|
votes={votes}
|
||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
|
selectedVisibilityType={visibilityType}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import type { UseChatHelpers } from '@ai-sdk/react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { ArrowDown } from 'lucide-react';
|
import { ArrowDown } from 'lucide-react';
|
||||||
import { useScrollToBottom } from '@/hooks/use-scroll-to-bottom';
|
import { useScrollToBottom } from '@/hooks/use-scroll-to-bottom';
|
||||||
|
import type { VisibilityType } from './visibility-selector';
|
||||||
|
|
||||||
function PureMultimodalInput({
|
function PureMultimodalInput({
|
||||||
chatId,
|
chatId,
|
||||||
|
|
@ -40,6 +41,7 @@ function PureMultimodalInput({
|
||||||
append,
|
append,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
className,
|
className,
|
||||||
|
selectedVisibilityType,
|
||||||
}: {
|
}: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
input: UseChatHelpers['input'];
|
input: UseChatHelpers['input'];
|
||||||
|
|
@ -53,6 +55,7 @@ function PureMultimodalInput({
|
||||||
append: UseChatHelpers['append'];
|
append: UseChatHelpers['append'];
|
||||||
handleSubmit: UseChatHelpers['handleSubmit'];
|
handleSubmit: UseChatHelpers['handleSubmit'];
|
||||||
className?: string;
|
className?: string;
|
||||||
|
selectedVisibilityType: VisibilityType;
|
||||||
}) {
|
}) {
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
|
|
@ -220,7 +223,11 @@ function PureMultimodalInput({
|
||||||
{messages.length === 0 &&
|
{messages.length === 0 &&
|
||||||
attachments.length === 0 &&
|
attachments.length === 0 &&
|
||||||
uploadQueue.length === 0 && (
|
uploadQueue.length === 0 && (
|
||||||
<SuggestedActions append={append} chatId={chatId} />
|
<SuggestedActions
|
||||||
|
append={append}
|
||||||
|
chatId={chatId}
|
||||||
|
selectedVisibilityType={selectedVisibilityType}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
|
@ -309,6 +316,8 @@ export const MultimodalInput = memo(
|
||||||
if (prevProps.input !== nextProps.input) return false;
|
if (prevProps.input !== nextProps.input) return false;
|
||||||
if (prevProps.status !== nextProps.status) return false;
|
if (prevProps.status !== nextProps.status) return false;
|
||||||
if (!equal(prevProps.attachments, nextProps.attachments)) return false;
|
if (!equal(prevProps.attachments, nextProps.attachments)) return false;
|
||||||
|
if (prevProps.selectedVisibilityType !== nextProps.selectedVisibilityType)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Chat } from '@/lib/db/schema';
|
import type { Chat } from '@/lib/db/schema';
|
||||||
import {
|
import {
|
||||||
SidebarMenuAction,
|
SidebarMenuAction,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
|
|
@ -39,7 +39,7 @@ const PureChatItem = ({
|
||||||
}) => {
|
}) => {
|
||||||
const { visibilityType, setVisibilityType } = useChatVisibility({
|
const { visibilityType, setVisibilityType } = useChatVisibility({
|
||||||
chatId: chat.id,
|
chatId: chat.id,
|
||||||
initialVisibility: chat.visibility,
|
initialVisibilityType: chat.visibility,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,20 @@
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Button } from './ui/button';
|
import { Button } from './ui/button';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { UseChatHelpers } from '@ai-sdk/react';
|
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||||
|
import type { VisibilityType } from './visibility-selector';
|
||||||
|
|
||||||
interface SuggestedActionsProps {
|
interface SuggestedActionsProps {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
append: UseChatHelpers['append'];
|
append: UseChatHelpers['append'];
|
||||||
|
selectedVisibilityType: VisibilityType;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PureSuggestedActions({ chatId, append }: SuggestedActionsProps) {
|
function PureSuggestedActions({
|
||||||
|
chatId,
|
||||||
|
append,
|
||||||
|
selectedVisibilityType,
|
||||||
|
}: SuggestedActionsProps) {
|
||||||
const suggestedActions = [
|
const suggestedActions = [
|
||||||
{
|
{
|
||||||
title: 'What are the advantages',
|
title: 'What are the advantages',
|
||||||
|
|
@ -71,4 +77,13 @@ function PureSuggestedActions({ chatId, append }: SuggestedActionsProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SuggestedActions = memo(PureSuggestedActions, () => true);
|
export const SuggestedActions = memo(
|
||||||
|
PureSuggestedActions,
|
||||||
|
(prevProps, nextProps) => {
|
||||||
|
if (prevProps.chatId !== nextProps.chatId) return false;
|
||||||
|
if (prevProps.selectedVisibilityType !== nextProps.selectedVisibilityType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ReactNode, useMemo, useState } from 'react';
|
import { type ReactNode, useMemo, useState } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|
@ -9,7 +9,6 @@ import {
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CheckCircleFillIcon,
|
CheckCircleFillIcon,
|
||||||
ChevronDownIcon,
|
ChevronDownIcon,
|
||||||
|
|
@ -52,7 +51,7 @@ export function VisibilitySelector({
|
||||||
|
|
||||||
const { visibilityType, setVisibilityType } = useChatVisibility({
|
const { visibilityType, setVisibilityType } = useChatVisibility({
|
||||||
chatId,
|
chatId,
|
||||||
initialVisibility: selectedVisibilityType,
|
initialVisibilityType: selectedVisibilityType,
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedVisibility = useMemo(
|
const selectedVisibility = useMemo(
|
||||||
|
|
@ -70,6 +69,7 @@ export function VisibilitySelector({
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
data-testid="visibility-selector"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="hidden md:flex md:px-2 md:h-[34px]"
|
className="hidden md:flex md:px-2 md:h-[34px]"
|
||||||
>
|
>
|
||||||
|
|
@ -82,6 +82,7 @@ export function VisibilitySelector({
|
||||||
<DropdownMenuContent align="start" className="min-w-[300px]">
|
<DropdownMenuContent align="start" className="min-w-[300px]">
|
||||||
{visibilities.map((visibility) => (
|
{visibilities.map((visibility) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
|
data-testid={`visibility-selector-item-${visibility.id}`}
|
||||||
key={visibility.id}
|
key={visibility.id}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
setVisibilityType(visibility.id);
|
setVisibilityType(visibility.id);
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import type { VisibilityType } from '@/components/visibility-selector';
|
||||||
|
|
||||||
export function useChatVisibility({
|
export function useChatVisibility({
|
||||||
chatId,
|
chatId,
|
||||||
initialVisibility,
|
initialVisibilityType,
|
||||||
}: {
|
}: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
initialVisibility: VisibilityType;
|
initialVisibilityType: VisibilityType;
|
||||||
}) {
|
}) {
|
||||||
const { mutate, cache } = useSWRConfig();
|
const { mutate, cache } = useSWRConfig();
|
||||||
const history: ChatHistory = cache.get('/api/history')?.data;
|
const history: ChatHistory = cache.get('/api/history')?.data;
|
||||||
|
|
@ -24,7 +24,7 @@ export function useChatVisibility({
|
||||||
`${chatId}-visibility`,
|
`${chatId}-visibility`,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
fallbackData: initialVisibility,
|
fallbackData: initialVisibilityType,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import {
|
||||||
import type { ArtifactKind } from '@/components/artifact';
|
import type { ArtifactKind } from '@/components/artifact';
|
||||||
import { generateUUID } from '../utils';
|
import { generateUUID } from '../utils';
|
||||||
import { generateHashedPassword } from './utils';
|
import { generateHashedPassword } from './utils';
|
||||||
|
import type { VisibilityType } from '@/components/visibility-selector';
|
||||||
|
|
||||||
// Optionally, if not using email/pass login, you can
|
// Optionally, if not using email/pass login, you can
|
||||||
// use the Drizzle adapter for Auth.js / NextAuth
|
// use the Drizzle adapter for Auth.js / NextAuth
|
||||||
|
|
@ -79,10 +80,12 @@ export async function saveChat({
|
||||||
id,
|
id,
|
||||||
userId,
|
userId,
|
||||||
title,
|
title,
|
||||||
|
visibility,
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
visibility: VisibilityType;
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
return await db.insert(chat).values({
|
return await db.insert(chat).values({
|
||||||
|
|
@ -90,6 +93,7 @@ export async function saveChat({
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
userId,
|
userId,
|
||||||
title,
|
title,
|
||||||
|
visibility,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save chat in database');
|
console.error('Failed to save chat in database');
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ai-chatbot",
|
"name": "ai-chatbot",
|
||||||
"version": "3.0.16",
|
"version": "3.0.17",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbo",
|
"dev": "next dev --turbo",
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,23 @@ export class ChatPage {
|
||||||
expect(await this.getSelectedModel()).toBe(chatModel.name);
|
expect(await this.getSelectedModel()).toBe(chatModel.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getSelectedVisibility() {
|
||||||
|
const visibilityId = await this.page
|
||||||
|
.getByTestId('visibility-selector')
|
||||||
|
.innerText();
|
||||||
|
return visibilityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async chooseVisibilityFromSelector(
|
||||||
|
chatVisibility: 'public' | 'private',
|
||||||
|
) {
|
||||||
|
await this.page.getByTestId('visibility-selector').click();
|
||||||
|
await this.page
|
||||||
|
.getByTestId(`visibility-selector-item-${chatVisibility}`)
|
||||||
|
.click();
|
||||||
|
expect(await this.getSelectedVisibility()).toBe(chatVisibility);
|
||||||
|
}
|
||||||
|
|
||||||
async getRecentAssistantMessage() {
|
async getRecentAssistantMessage() {
|
||||||
const messageElements = await this.page
|
const messageElements = await this.page
|
||||||
.getByTestId('message-assistant')
|
.getByTestId('message-assistant')
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ test.describe
|
||||||
id: chatId,
|
id: chatId,
|
||||||
message: TEST_PROMPTS.SKY.MESSAGE,
|
message: TEST_PROMPTS.SKY.MESSAGE,
|
||||||
selectedChatModel: 'chat-model',
|
selectedChatModel: 'chat-model',
|
||||||
|
selectedVisibilityType: 'private',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(response.status()).toBe(200);
|
expect(response.status()).toBe(200);
|
||||||
|
|
@ -49,6 +50,7 @@ test.describe
|
||||||
id: chatId,
|
id: chatId,
|
||||||
message: TEST_PROMPTS.GRASS.MESSAGE,
|
message: TEST_PROMPTS.GRASS.MESSAGE,
|
||||||
selectedChatModel: 'chat-model',
|
selectedChatModel: 'chat-model',
|
||||||
|
selectedVisibilityType: 'private',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(response.status()).toBe(403);
|
expect(response.status()).toBe(403);
|
||||||
|
|
@ -109,6 +111,7 @@ test.describe
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
selectedChatModel: 'chat-model',
|
selectedChatModel: 'chat-model',
|
||||||
|
selectedVisibilityType: 'private',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -162,6 +165,7 @@ test.describe
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
selectedChatModel: 'chat-model',
|
selectedChatModel: 'chat-model',
|
||||||
|
selectedVisibilityType: 'private',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -190,7 +194,7 @@ test.describe
|
||||||
expect(secondResponseContent).toEqual('');
|
expect(secondResponseContent).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Babbage cannot resume chat generation that belongs to Ada', async ({
|
test('Babbage cannot resume a private chat generation that belongs to Ada', async ({
|
||||||
adaContext,
|
adaContext,
|
||||||
babbageContext,
|
babbageContext,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -212,6 +216,7 @@ test.describe
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
selectedChatModel: 'chat-model',
|
selectedChatModel: 'chat-model',
|
||||||
|
selectedVisibilityType: 'private',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -234,4 +239,57 @@ test.describe
|
||||||
expect(firstStatusCode).toBe(200);
|
expect(firstStatusCode).toBe(200);
|
||||||
expect(secondStatusCode).toBe(403);
|
expect(secondStatusCode).toBe(403);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Babbage can resume a public chat generation that belongs to Ada', async ({
|
||||||
|
adaContext,
|
||||||
|
babbageContext,
|
||||||
|
}) => {
|
||||||
|
const chatId = generateUUID();
|
||||||
|
|
||||||
|
const firstRequest = adaContext.request.post('/api/chat', {
|
||||||
|
data: {
|
||||||
|
id: chatId,
|
||||||
|
message: {
|
||||||
|
id: generateUUID(),
|
||||||
|
role: 'user',
|
||||||
|
content: 'Help me write an essay about Silicon Valley',
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Help me write an essay about Silicon Valley',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
selectedChatModel: 'chat-model',
|
||||||
|
selectedVisibilityType: 'public',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
const secondRequest = babbageContext.request.get(
|
||||||
|
`/api/chat?chatId=${chatId}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [firstResponse, secondResponse] = await Promise.all([
|
||||||
|
firstRequest,
|
||||||
|
secondRequest,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [firstStatusCode, secondStatusCode] = await Promise.all([
|
||||||
|
firstResponse.status(),
|
||||||
|
secondResponse.status(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(firstStatusCode).toBe(200);
|
||||||
|
expect(secondStatusCode).toBe(200);
|
||||||
|
|
||||||
|
const [firstResponseContent, secondResponseContent] = await Promise.all([
|
||||||
|
firstResponse.text(),
|
||||||
|
secondResponse.text(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(firstResponseContent).toEqual(secondResponseContent);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue