Update examples and improve stability (#271)
This commit is contained in:
parent
25e4128e25
commit
70ed5c4f47
16 changed files with 118 additions and 101 deletions
5
app/new/page.tsx
Normal file
5
app/new/page.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { redirect } from 'next/navigation'
|
||||||
|
|
||||||
|
export default async function NewPage() {
|
||||||
|
redirect('/')
|
||||||
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ export default async function SharePage({ params }: SharePageProps) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AI>
|
<AI>
|
||||||
<ChatList messages={uiState} />
|
<ChatList messages={uiState} isShared={true} />
|
||||||
</AI>
|
</AI>
|
||||||
</div>
|
</div>
|
||||||
<FooterText className="py-8" />
|
<FooterText className="py-8" />
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,37 @@
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import { UIState } from '@/lib/chat/actions'
|
import { UIState } from '@/lib/chat/actions'
|
||||||
|
import { Session } from '@/lib/types'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
export interface ChatList {
|
export interface ChatList {
|
||||||
messages: UIState
|
messages: UIState
|
||||||
|
session?: Session
|
||||||
|
isShared: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatList({ messages }: ChatList) {
|
export function ChatList({ messages, session, isShared }: ChatList) {
|
||||||
if (!messages.length) {
|
if (!messages.length) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative mx-auto max-w-2xl px-4">
|
<div className="relative mx-auto max-w-2xl px-4">
|
||||||
|
{!isShared && !session ? (
|
||||||
|
<div className="mb-8 rounded-lg border bg-white p-4 dark:bg-zinc-950">
|
||||||
|
<p className="text-muted-foreground leading-normal">
|
||||||
|
Please{' '}
|
||||||
|
<Link href="/login" className="underline">
|
||||||
|
log in
|
||||||
|
</Link>{' '}
|
||||||
|
or{' '}
|
||||||
|
<Link href="/signup" className="underline">
|
||||||
|
sign up
|
||||||
|
</Link>{' '}
|
||||||
|
to save and revisit your chat history!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{messages.map((message, index) => (
|
{messages.map((message, index) => (
|
||||||
<div key={message.id}>
|
<div key={message.id}>
|
||||||
{message.display}
|
{message.display}
|
||||||
|
|
|
||||||
|
|
@ -27,24 +27,24 @@ export function ChatPanel({ id, title, input, setInput }: ChatPanelProps) {
|
||||||
|
|
||||||
const exampleMessages = [
|
const exampleMessages = [
|
||||||
{
|
{
|
||||||
heading: 'Explain the concept',
|
heading: 'What are the',
|
||||||
subheading: 'of a serverless function',
|
subheading: 'trending memecoins today?',
|
||||||
message: `Explain the concept of a serverless function`
|
message: `What are the trending memecoins today?`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: 'What are the benefits',
|
heading: 'What is the price of',
|
||||||
subheading: 'of using turborepo in my codebase?',
|
subheading: 'DOGE in the stock market?',
|
||||||
message: 'What are the benefits of using turborepo in my codebase?'
|
message: 'What is the price of DOGE in the stock market?'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: 'List differences between',
|
heading: 'I would like to buy',
|
||||||
subheading: 'pages and app router in Next.js',
|
subheading: '42 DOGE coins',
|
||||||
message: `List differences between pages and app router in Next.js`
|
message: `I would like to buy 42 DOGE coins`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: 'What is the price',
|
heading: 'What are some',
|
||||||
subheading: `of VRCL in the stock market?`,
|
subheading: `recent events about DOGE?`,
|
||||||
message: `What is the price of VRCL in the stock market?`
|
message: `What are some recent events about DOGE?`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export function Chat({ id, className, session, missingKeys }: ChatProps) {
|
||||||
<div className={cn('pb-[200px] pt-4 md:pt-10', className)}>
|
<div className={cn('pb-[200px] pt-4 md:pt-10', className)}>
|
||||||
{messages.length ? (
|
{messages.length ? (
|
||||||
<>
|
<>
|
||||||
<ChatList messages={messages} />
|
<ChatList messages={messages} isShared={false} session={session} />
|
||||||
<ChatScrollAnchor trackVisibility={isLoading} />
|
<ChatScrollAnchor trackVisibility={isLoading} />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export function EmptyScreen({ setInput }: Pick<UseChatHelpers, 'setInput'>) {
|
||||||
<ExternalLink href="https://vercel.com/blog/ai-sdk-3-generative-ui">
|
<ExternalLink href="https://vercel.com/blog/ai-sdk-3-generative-ui">
|
||||||
React Server Components
|
React Server Components
|
||||||
</ExternalLink>{' '}
|
</ExternalLink>{' '}
|
||||||
to combine text with UI generated as output of the LLM. The UI state
|
to combine text with generative UI as output of the LLM. The UI state
|
||||||
is synced through the SDK so the model is aware of your interactions
|
is synced through the SDK so the model is aware of your interactions
|
||||||
as they happen.
|
as they happen.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ export function FooterText({ className, ...props }: React.ComponentProps<'p'>) {
|
||||||
>
|
>
|
||||||
Open source AI chatbot built with{' '}
|
Open source AI chatbot built with{' '}
|
||||||
<ExternalLink href="https://nextjs.org">Next.js</ExternalLink> and{' '}
|
<ExternalLink href="https://nextjs.org">Next.js</ExternalLink> and{' '}
|
||||||
<ExternalLink href="https://vercel.com/storage/kv">
|
<ExternalLink href="https://github.com/vercel/ai">
|
||||||
Vercel KV
|
Vercel AI SDK
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ async function UserOrLogin() {
|
||||||
<SidebarToggle />
|
<SidebarToggle />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Link href="/" rel="nofollow">
|
<Link href="/new" rel="nofollow">
|
||||||
<IconNextChat className="size-6 mr-2 dark:hidden" inverted />
|
<IconNextChat className="size-6 mr-2 dark:hidden" inverted />
|
||||||
<IconNextChat className="hidden size-6 mr-2 dark:block" />
|
<IconNextChat className="hidden size-6 mr-2 dark:block" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,9 @@ export function PromptForm({
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="absolute left-0 top-4 size-8 rounded-full bg-background p-0 sm:left-4"
|
className="absolute left-0 top-[14px] size-8 rounded-full bg-background p-0 sm:left-4"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push('/')
|
router.push('/new')
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconPlus />
|
<IconPlus />
|
||||||
|
|
@ -98,7 +98,7 @@ export function PromptForm({
|
||||||
value={input}
|
value={input}
|
||||||
onChange={e => setInput(e.target.value)}
|
onChange={e => setInput(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="absolute right-0 top-4 sm:right-4">
|
<div className="absolute right-0 top-[13px] sm:right-4">
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button type="submit" size="icon" disabled={input === ''}>
|
<Button type="submit" size="icon" disabled={input === ''}>
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,13 @@ const placeholderEvents = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export const EventsSkeleton = ({ events = placeholderEvents }) => {
|
export const EventsSkeleton = () => {
|
||||||
return (
|
return (
|
||||||
<div className="-mt-2 flex flex-col gap-2 py-4">
|
<div className="-mt-2 flex w-full flex-col gap-2 py-4">
|
||||||
{placeholderEvents.map(event => (
|
{placeholderEvents.map(event => (
|
||||||
<div
|
<div
|
||||||
key={event.date}
|
key={event.date}
|
||||||
className="max-w-96 flex gap-1 shrink-0 flex-col rounded-lg bg-zinc-800 p-4"
|
className="flex shrink-0 flex-col gap-1 rounded-lg bg-zinc-800 p-4"
|
||||||
>
|
>
|
||||||
<div className="w-fit rounded-md bg-zinc-700 text-sm text-transparent">
|
<div className="w-fit rounded-md bg-zinc-700 text-sm text-transparent">
|
||||||
{event.date}
|
{event.date}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ interface Event {
|
||||||
|
|
||||||
export function Events({ props: events }: { props: Event[] }) {
|
export function Events({ props: events }: { props: Event[] }) {
|
||||||
return (
|
return (
|
||||||
<div className="-mt-2 flex flex-col gap-2">
|
<div className="-mt-2 flex w-full flex-col gap-2 py-4">
|
||||||
{events.map(event => (
|
{events.map(event => (
|
||||||
<div
|
<div
|
||||||
key={event.date}
|
key={event.date}
|
||||||
className="max-w-96 flex shrink-0 flex-col rounded-lg bg-zinc-800 p-4 gap-1"
|
className="flex shrink-0 flex-col gap-1 rounded-lg bg-zinc-800 p-4"
|
||||||
>
|
>
|
||||||
<div className="text-sm text-zinc-400">
|
<div className="text-sm text-zinc-400">
|
||||||
{format(parseISO(event.date), 'dd LLL, yyyy')}
|
{format(parseISO(event.date), 'dd LLL, yyyy')}
|
||||||
|
|
@ -18,9 +18,7 @@ const Purchase = dynamic(
|
||||||
{
|
{
|
||||||
ssr: false,
|
ssr: false,
|
||||||
loading: () => (
|
loading: () => (
|
||||||
<div className="rounded-lg bg-zinc-900 px-4 py-5 text-center text-xs">
|
<div className="h-[375px] rounded-xl border bg-zinc-950 p-4 text-green-400 sm:h-[314px]" />
|
||||||
Loading stock info...
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -30,7 +28,7 @@ const Stocks = dynamic(() => import('./stocks').then(mod => mod.Stocks), {
|
||||||
loading: () => <StocksSkeleton />
|
loading: () => <StocksSkeleton />
|
||||||
})
|
})
|
||||||
|
|
||||||
const Events = dynamic(() => import('./event').then(mod => mod.Events), {
|
const Events = dynamic(() => import('./events').then(mod => mod.Events), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
loading: () => <EventsSkeleton />
|
loading: () => <EventsSkeleton />
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,18 @@ import { formatNumber } from '@/lib/utils'
|
||||||
import type { AI } from '@/lib/chat/actions'
|
import type { AI } from '@/lib/chat/actions'
|
||||||
|
|
||||||
interface Purchase {
|
interface Purchase {
|
||||||
defaultAmount?: number
|
numberOfShares?: number
|
||||||
name: string
|
symbol: string
|
||||||
price: number
|
price: number
|
||||||
status: 'requires_action' | 'completed' | 'expired'
|
status: 'requires_action' | 'completed' | 'expired'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Purchase({
|
export function Purchase({
|
||||||
props: { defaultAmount, name, price, status = 'expired' }
|
props: { numberOfShares, symbol, price, status = 'requires_action' }
|
||||||
}: {
|
}: {
|
||||||
props: Purchase
|
props: Purchase
|
||||||
}) {
|
}) {
|
||||||
const [value, setValue] = useState(defaultAmount || 100)
|
const [value, setValue] = useState(numberOfShares || 100)
|
||||||
const [purchasingUI, setPurchasingUI] = useState<null | React.ReactNode>(null)
|
const [purchasingUI, setPurchasingUI] = useState<null | React.ReactNode>(null)
|
||||||
const [aiState, setAIState] = useAIState<typeof AI>()
|
const [aiState, setAIState] = useAIState<typeof AI>()
|
||||||
const [, setMessages] = useUIState<typeof AI>()
|
const [, setMessages] = useUIState<typeof AI>()
|
||||||
|
|
@ -60,11 +60,11 @@ export function Purchase({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl border bg-zinc-950 p-4 text-green-400 mt-2">
|
<div className="rounded-xl border bg-zinc-950 p-4 text-green-400">
|
||||||
<div className="float-right inline-block rounded-full bg-white/10 px-2 py-1 text-xs">
|
<div className="float-right inline-block rounded-full bg-white/10 px-2 py-1 text-xs">
|
||||||
+1.23% ↑
|
+1.23% ↑
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg text-zinc-300">{name}</div>
|
<div className="text-lg text-zinc-300">{symbol}</div>
|
||||||
<div className="text-3xl font-bold">${price}</div>
|
<div className="text-3xl font-bold">${price}</div>
|
||||||
{purchasingUI ? (
|
{purchasingUI ? (
|
||||||
<div className="mt-4 text-zinc-200">{purchasingUI}</div>
|
<div className="mt-4 text-zinc-200">{purchasingUI}</div>
|
||||||
|
|
@ -100,14 +100,14 @@ export function Purchase({
|
||||||
<div className="flex flex-wrap items-center text-xl font-bold sm:items-end sm:gap-2 sm:text-3xl">
|
<div className="flex flex-wrap items-center text-xl font-bold sm:items-end sm:gap-2 sm:text-3xl">
|
||||||
<div className="flex basis-1/3 flex-col tabular-nums sm:basis-auto sm:flex-row sm:items-center sm:gap-2">
|
<div className="flex basis-1/3 flex-col tabular-nums sm:basis-auto sm:flex-row sm:items-center sm:gap-2">
|
||||||
{value}
|
{value}
|
||||||
<span className="mb-1 text-sm font-normal text-zinc-600 dark:text-zinc-400 sm:mb-0">
|
<span className="mb-1 text-sm font-normal text-zinc-600 sm:mb-0 dark:text-zinc-400">
|
||||||
shares
|
shares
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="basis-1/3 text-center sm:basis-auto">×</div>
|
<div className="basis-1/3 text-center sm:basis-auto">×</div>
|
||||||
<span className="flex basis-1/3 flex-col tabular-nums sm:basis-auto sm:flex-row sm:items-center sm:gap-2">
|
<span className="flex basis-1/3 flex-col tabular-nums sm:basis-auto sm:flex-row sm:items-center sm:gap-2">
|
||||||
${price}
|
${price}
|
||||||
<span className="mb-1 ml-1 text-sm font-normal text-zinc-600 dark:text-zinc-400 sm:mb-0">
|
<span className="mb-1 ml-1 text-sm font-normal text-zinc-600 sm:mb-0 dark:text-zinc-400">
|
||||||
per share
|
per share
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -118,9 +118,9 @@ export function Purchase({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="mt-6 w-full rounded-lg bg-green-500 px-4 py-2 text-zinc-900 dark:bg-green-500"
|
className="mt-6 w-full rounded-lg bg-green-500 px-4 py-2 font-bold text-zinc-900 hover:bg-green-600"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const response = await confirmPurchase(name, price, value)
|
const response = await confirmPurchase(symbol, price, value)
|
||||||
setPurchasingUI(response.purchasingUI)
|
setPurchasingUI(response.purchasingUI)
|
||||||
|
|
||||||
// Insert a new system message to the UI.
|
// Insert a new system message to the UI.
|
||||||
|
|
@ -135,7 +135,7 @@ export function Purchase({
|
||||||
</>
|
</>
|
||||||
) : status === 'completed' ? (
|
) : status === 'completed' ? (
|
||||||
<p className="mb-2 text-white">
|
<p className="mb-2 text-white">
|
||||||
You have successfully purchased {value} ${name}. Total cost:{' '}
|
You have successfully purchased {value} ${symbol}. Total cost:{' '}
|
||||||
{formatNumber(value * price)}
|
{formatNumber(value * price)}
|
||||||
</p>
|
</p>
|
||||||
) : status === 'expired' ? (
|
) : status === 'expired' ? (
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
export const StocksSkeleton = () => {
|
export const StocksSkeleton = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mb-4 flex flex-col gap-2 overflow-y-scroll pb-4 text-sm sm:flex-row">
|
<div className="mb-4 flex flex-col gap-2 overflow-y-scroll pb-4 text-sm sm:flex-row">
|
||||||
<div className="flex h-[60px] w-full cursor-pointer flex-row gap-2 rounded-lg bg-zinc-900 p-2 text-left hover:bg-zinc-800 sm:w-[208px]"></div>
|
<div className="flex h-[60px] w-full cursor-pointer flex-row gap-2 rounded-lg bg-zinc-800 p-2 text-left hover:bg-zinc-800 sm:w-[208px]"></div>
|
||||||
<div className="flex h-[60px] w-full cursor-pointer flex-row gap-2 rounded-lg bg-zinc-900 p-2 text-left hover:bg-zinc-800 sm:w-[208px]"></div>
|
<div className="flex h-[60px] w-full cursor-pointer flex-row gap-2 rounded-lg bg-zinc-800 p-2 text-left hover:bg-zinc-800 sm:w-[208px]"></div>
|
||||||
<div className="flex h-[60px] w-full cursor-pointer flex-row gap-2 rounded-lg bg-zinc-900 p-2 text-left hover:bg-zinc-800 sm:w-[208px]"></div>
|
<div className="flex h-[60px] w-full cursor-pointer flex-row gap-2 rounded-lg bg-zinc-800 p-2 text-left hover:bg-zinc-800 sm:w-[208px]"></div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,45 +15,53 @@ export function Stocks({ props: stocks }: { props: Stock[] }) {
|
||||||
const { submitUserMessage } = useActions()
|
const { submitUserMessage } = useActions()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-4 flex flex-col gap-2 overflow-y-scroll pb-4 text-sm sm:flex-row">
|
<div>
|
||||||
{stocks.map(stock => (
|
<div className="mb-4 flex flex-col gap-2 overflow-y-scroll pb-4 text-sm sm:flex-row">
|
||||||
<button
|
{stocks.map(stock => (
|
||||||
key={stock.symbol}
|
<button
|
||||||
className="flex cursor-pointer flex-row gap-2 rounded-lg bg-zinc-800 p-2 text-left hover:bg-zinc-700 sm:w-52"
|
key={stock.symbol}
|
||||||
onClick={async () => {
|
className="flex cursor-pointer flex-row gap-2 rounded-lg bg-zinc-800 p-2 text-left hover:bg-zinc-700 sm:w-52"
|
||||||
const response = await submitUserMessage(`View ${stock.symbol}`)
|
onClick={async () => {
|
||||||
setMessages(currentMessages => [...currentMessages, response])
|
const response = await submitUserMessage(`View ${stock.symbol}`)
|
||||||
}}
|
setMessages(currentMessages => [...currentMessages, response])
|
||||||
>
|
}}
|
||||||
<div
|
|
||||||
className={`text-xl ${
|
|
||||||
stock.delta > 0 ? 'text-green-600' : 'text-red-600'
|
|
||||||
} flex w-11 flex-row justify-center rounded-md bg-white/10 p-2`}
|
|
||||||
>
|
>
|
||||||
{stock.delta > 0 ? '↑' : '↓'}
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="bold uppercase text-zinc-300">{stock.symbol}</div>
|
|
||||||
<div className="text-base text-zinc-500">${stock.price}</div>
|
|
||||||
</div>
|
|
||||||
<div className="ml-auto flex flex-col">
|
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`text-xl ${
|
||||||
stock.delta > 0 ? 'text-green-600' : 'text-red-600'
|
stock.delta > 0 ? 'text-green-600' : 'text-red-600'
|
||||||
} bold text-right uppercase`}
|
} flex w-11 flex-row justify-center rounded-md bg-white/10 p-2`}
|
||||||
>
|
>
|
||||||
{` ${((stock.delta / stock.price) * 100).toFixed(2)}%`}
|
{stock.delta > 0 ? '↑' : '↓'}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="flex flex-col">
|
||||||
className={`${
|
<div className="bold uppercase text-zinc-300">{stock.symbol}</div>
|
||||||
stock.delta > 0 ? 'text-green-700' : 'text-red-700'
|
<div className="text-base text-zinc-500">
|
||||||
} text-right text-base`}
|
${stock.price.toExponential(1)}
|
||||||
>
|
</div>
|
||||||
{stock.delta}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="ml-auto flex flex-col">
|
||||||
</button>
|
<div
|
||||||
))}
|
className={`${
|
||||||
|
stock.delta > 0 ? 'text-green-600' : 'text-red-600'
|
||||||
|
} bold text-right uppercase`}
|
||||||
|
>
|
||||||
|
{` ${((stock.delta / stock.price) * 100).toExponential(1)}%`}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
stock.delta > 0 ? 'text-green-700' : 'text-red-700'
|
||||||
|
} text-right text-base`}
|
||||||
|
>
|
||||||
|
{stock.delta.toExponential(1)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="p-4 text-center text-sm text-zinc-500">
|
||||||
|
Note: Data and latency are simulated for illustrative purposes and
|
||||||
|
should not be considered as financial advice.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import {
|
||||||
|
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { EventsSkeleton } from '@/components/stocks/events-skeleton'
|
import { EventsSkeleton } from '@/components/stocks/events-skeleton'
|
||||||
import { Events } from '@/components/stocks/event'
|
import { Events } from '@/components/stocks/events'
|
||||||
import { StocksSkeleton } from '@/components/stocks/stocks-skeleton'
|
import { StocksSkeleton } from '@/components/stocks/stocks-skeleton'
|
||||||
import { Stocks } from '@/components/stocks/stocks'
|
import { Stocks } from '@/components/stocks/stocks'
|
||||||
import { StockSkeleton } from '@/components/stocks/stock-skeleton'
|
import { StockSkeleton } from '@/components/stocks/stock-skeleton'
|
||||||
|
|
@ -95,7 +95,7 @@ async function confirmPurchase(symbol: string, price: number, amount: number) {
|
||||||
role: 'function',
|
role: 'function',
|
||||||
name: 'showStockPurchase',
|
name: 'showStockPurchase',
|
||||||
content: JSON.stringify({
|
content: JSON.stringify({
|
||||||
name: symbol,
|
symbol,
|
||||||
price,
|
price,
|
||||||
defaultAmount: amount,
|
defaultAmount: amount,
|
||||||
status: 'completed'
|
status: 'completed'
|
||||||
|
|
@ -328,25 +328,16 @@ Besides that, you can also chat with users and do some calculations if needed.`
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<BotCard>
|
||||||
<BotMessage
|
|
||||||
content={`Sure!
|
|
||||||
${
|
|
||||||
typeof numberOfShares === 'number'
|
|
||||||
? `Click the button below to purchase ${numberOfShares} shares of $${symbol}:`
|
|
||||||
: `How many $${symbol} would you like to purchase?`
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Purchase
|
<Purchase
|
||||||
props={{
|
props={{
|
||||||
defaultAmount: numberOfShares,
|
numberOfShares,
|
||||||
name: symbol,
|
symbol,
|
||||||
price: +price,
|
price: +price,
|
||||||
status: 'requires_action'
|
status: 'requires_action'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</BotCard>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -405,18 +396,13 @@ Besides that, you can also chat with users and do some calculations if needed.`
|
||||||
export type Message = {
|
export type Message = {
|
||||||
role: 'user' | 'assistant' | 'system' | 'function' | 'data' | 'tool'
|
role: 'user' | 'assistant' | 'system' | 'function' | 'data' | 'tool'
|
||||||
content: string
|
content: string
|
||||||
id?: string
|
id: string
|
||||||
name?: string
|
name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AIState = {
|
export type AIState = {
|
||||||
chatId: string
|
chatId: string
|
||||||
messages: {
|
messages: Message[]
|
||||||
role: 'user' | 'assistant' | 'system' | 'function' | 'data' | 'tool'
|
|
||||||
content: string
|
|
||||||
id: string
|
|
||||||
name?: string
|
|
||||||
}[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UIState = {
|
export type UIState = {
|
||||||
|
|
@ -447,7 +433,7 @@ export const AI = createAI<AIState, UIState>({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
unstable_onSetAIState: async ({ state }) => {
|
unstable_onSetAIState: async ({ state, done }) => {
|
||||||
'use server'
|
'use server'
|
||||||
|
|
||||||
const session = await auth()
|
const session = await auth()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue