langwatch 0.2.0 → 0.3.0-prerelease.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +16 -0
- package/LICENSE +7 -0
- package/README.md +268 -1
- package/copy-types.sh +19 -8
- package/examples/langchain/.env.example +2 -0
- package/examples/langchain/README.md +42 -0
- package/examples/langchain/package-lock.json +2930 -0
- package/examples/langchain/package.json +27 -0
- package/examples/langchain/src/cli-markdown.d.ts +137 -0
- package/examples/langchain/src/index.ts +109 -0
- package/examples/langchain/tsconfig.json +25 -0
- package/examples/langgraph/.env.example +2 -0
- package/examples/langgraph/README.md +42 -0
- package/examples/langgraph/package-lock.json +3031 -0
- package/examples/langgraph/package.json +28 -0
- package/examples/langgraph/src/cli-markdown.d.ts +137 -0
- package/examples/langgraph/src/index.ts +196 -0
- package/examples/langgraph/tsconfig.json +25 -0
- package/examples/mastra/.env.example +2 -0
- package/examples/mastra/README.md +57 -0
- package/examples/mastra/package-lock.json +5296 -0
- package/examples/mastra/package.json +32 -0
- package/examples/mastra/src/cli-markdown.d.ts +137 -0
- package/examples/mastra/src/index.ts +120 -0
- package/examples/mastra/src/mastra/agents/weather-agent.ts +30 -0
- package/examples/mastra/src/mastra/index.ts +21 -0
- package/examples/mastra/src/mastra/tools/weather-tool.ts +102 -0
- package/examples/mastra/tsconfig.json +25 -0
- package/examples/vercel-ai/.env.example +2 -0
- package/examples/vercel-ai/README.md +38 -0
- package/examples/vercel-ai/package-lock.json +2571 -0
- package/examples/vercel-ai/package.json +27 -0
- package/examples/vercel-ai/src/cli-markdown.d.ts +137 -0
- package/examples/vercel-ai/src/index.ts +110 -0
- package/examples/vercel-ai/src/instrumentation.ts +9 -0
- package/examples/vercel-ai/tsconfig.json +25 -0
- package/package.json +78 -34
- package/src/__tests__/client-browser.test.ts +92 -0
- package/src/__tests__/client-node.test.ts +76 -0
- package/src/__tests__/client.test.ts +71 -0
- package/src/__tests__/integration/client-browser.test.ts +46 -0
- package/src/__tests__/integration/client-node.test.ts +46 -0
- package/src/client-browser.ts +70 -0
- package/src/client-node.ts +82 -0
- package/src/client-shared.ts +72 -0
- package/src/client.ts +119 -0
- package/src/evaluation/__tests__/record-evaluation.test.ts +112 -0
- package/src/evaluation/__tests__/run-evaluation.test.ts +171 -0
- package/src/evaluation/index.ts +2 -0
- package/src/evaluation/record-evaluation.ts +101 -0
- package/src/evaluation/run-evaluation.ts +133 -0
- package/src/evaluation/tracer.ts +3 -0
- package/src/evaluation/types.ts +23 -0
- package/src/index.ts +10 -593
- package/src/internal/api/__tests__/errors.test.ts +98 -0
- package/src/internal/api/client.ts +30 -0
- package/src/internal/api/errors.ts +32 -0
- package/src/internal/generated/types/.gitkeep +0 -0
- package/src/observability/__tests__/integration/base.test.ts +74 -0
- package/src/observability/__tests__/integration/browser-setup-ordering.test.ts +60 -0
- package/src/observability/__tests__/integration/complex-nested-spans.test.ts +29 -0
- package/src/observability/__tests__/integration/error-handling.test.ts +24 -0
- package/src/observability/__tests__/integration/langwatch-disabled-otel.test.ts +24 -0
- package/src/observability/__tests__/integration/langwatch-first-then-vercel.test.ts +24 -0
- package/src/observability/__tests__/integration/multiple-setup-attempts.test.ts +27 -0
- package/src/observability/__tests__/integration/otel-ordering.test.ts +27 -0
- package/src/observability/__tests__/integration/vercel-configurations.test.ts +20 -0
- package/src/observability/__tests__/integration/vercel-first-then-langwatch.test.ts +27 -0
- package/src/observability/__tests__/span.test.ts +214 -0
- package/src/observability/__tests__/trace.test.ts +180 -0
- package/src/observability/exporters/index.ts +1 -0
- package/src/observability/exporters/langwatch-exporter.ts +53 -0
- package/src/observability/index.ts +4 -0
- package/src/observability/instrumentation/langchain/__tests__/integration/langchain-chatbot.test.ts +112 -0
- package/src/observability/instrumentation/langchain/__tests__/langchain.test.ts +284 -0
- package/src/observability/instrumentation/langchain/index.ts +624 -0
- package/src/observability/processors/__tests__/filterable-batch-span-exporter.test.ts +98 -0
- package/src/observability/processors/filterable-batch-span-processor.ts +99 -0
- package/src/observability/processors/index.ts +1 -0
- package/src/observability/semconv/attributes.ts +185 -0
- package/src/observability/semconv/events.ts +42 -0
- package/src/observability/semconv/index.ts +16 -0
- package/src/observability/semconv/values.ts +159 -0
- package/src/observability/span.ts +728 -0
- package/src/observability/trace.ts +301 -0
- package/src/prompt/__tests__/prompt.test.ts +139 -0
- package/src/prompt/get-prompt-version.ts +49 -0
- package/src/prompt/get-prompt.ts +44 -0
- package/src/prompt/index.ts +3 -0
- package/src/prompt/prompt.ts +133 -0
- package/src/prompt/service.ts +221 -0
- package/src/prompt/tracer.ts +3 -0
- package/src/prompt/types.ts +0 -0
- package/ts-to-zod.config.js +11 -0
- package/tsconfig.json +3 -9
- package/tsup.config.ts +11 -1
- package/vitest.config.ts +1 -0
- package/dist/chunk-LKD2K67J.mjs +0 -717
- package/dist/chunk-LKD2K67J.mjs.map +0 -1
- package/dist/index.d.mts +0 -1030
- package/dist/index.d.ts +0 -1030
- package/dist/index.js +0 -27310
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -963
- package/dist/index.mjs.map +0 -1
- package/dist/utils-Cv-rUjJ1.d.mts +0 -313
- package/dist/utils-Cv-rUjJ1.d.ts +0 -313
- package/dist/utils.d.mts +0 -2
- package/dist/utils.d.ts +0 -2
- package/dist/utils.js +0 -709
- package/dist/utils.js.map +0 -1
- package/dist/utils.mjs +0 -11
- package/dist/utils.mjs.map +0 -1
- package/example/.env.example +0 -12
- package/example/.eslintrc.json +0 -26
- package/example/LICENSE +0 -13
- package/example/README.md +0 -12
- package/example/app/(chat)/chat/[id]/page.tsx +0 -60
- package/example/app/(chat)/layout.tsx +0 -14
- package/example/app/(chat)/page.tsx +0 -27
- package/example/app/actions.ts +0 -156
- package/example/app/globals.css +0 -76
- package/example/app/guardrails/page.tsx +0 -26
- package/example/app/langchain/page.tsx +0 -27
- package/example/app/langchain-rag/page.tsx +0 -28
- package/example/app/late-update/page.tsx +0 -27
- package/example/app/layout.tsx +0 -64
- package/example/app/login/actions.ts +0 -71
- package/example/app/login/page.tsx +0 -18
- package/example/app/manual/page.tsx +0 -27
- package/example/app/new/page.tsx +0 -5
- package/example/app/opengraph-image.png +0 -0
- package/example/app/share/[id]/page.tsx +0 -58
- package/example/app/signup/actions.ts +0 -111
- package/example/app/signup/page.tsx +0 -18
- package/example/app/twitter-image.png +0 -0
- package/example/auth.config.ts +0 -42
- package/example/auth.ts +0 -45
- package/example/components/button-scroll-to-bottom.tsx +0 -36
- package/example/components/chat-history.tsx +0 -49
- package/example/components/chat-list.tsx +0 -52
- package/example/components/chat-message-actions.tsx +0 -40
- package/example/components/chat-message.tsx +0 -80
- package/example/components/chat-panel.tsx +0 -139
- package/example/components/chat-share-dialog.tsx +0 -95
- package/example/components/chat.tsx +0 -84
- package/example/components/clear-history.tsx +0 -75
- package/example/components/empty-screen.tsx +0 -38
- package/example/components/external-link.tsx +0 -29
- package/example/components/footer.tsx +0 -19
- package/example/components/header.tsx +0 -114
- package/example/components/login-button.tsx +0 -42
- package/example/components/login-form.tsx +0 -97
- package/example/components/markdown.tsx +0 -9
- package/example/components/prompt-form.tsx +0 -115
- package/example/components/providers.tsx +0 -17
- package/example/components/sidebar-actions.tsx +0 -125
- package/example/components/sidebar-desktop.tsx +0 -19
- package/example/components/sidebar-footer.tsx +0 -16
- package/example/components/sidebar-item.tsx +0 -124
- package/example/components/sidebar-items.tsx +0 -42
- package/example/components/sidebar-list.tsx +0 -38
- package/example/components/sidebar-mobile.tsx +0 -31
- package/example/components/sidebar-toggle.tsx +0 -24
- package/example/components/sidebar.tsx +0 -21
- package/example/components/signup-form.tsx +0 -95
- package/example/components/stocks/events-skeleton.tsx +0 -31
- package/example/components/stocks/events.tsx +0 -30
- package/example/components/stocks/index.tsx +0 -36
- package/example/components/stocks/message.tsx +0 -134
- package/example/components/stocks/spinner.tsx +0 -16
- package/example/components/stocks/stock-purchase.tsx +0 -146
- package/example/components/stocks/stock-skeleton.tsx +0 -22
- package/example/components/stocks/stock.tsx +0 -210
- package/example/components/stocks/stocks-skeleton.tsx +0 -9
- package/example/components/stocks/stocks.tsx +0 -67
- package/example/components/tailwind-indicator.tsx +0 -14
- package/example/components/theme-toggle.tsx +0 -31
- package/example/components/ui/alert-dialog.tsx +0 -141
- package/example/components/ui/badge.tsx +0 -36
- package/example/components/ui/button.tsx +0 -57
- package/example/components/ui/codeblock.tsx +0 -148
- package/example/components/ui/dialog.tsx +0 -122
- package/example/components/ui/dropdown-menu.tsx +0 -205
- package/example/components/ui/icons.tsx +0 -507
- package/example/components/ui/input.tsx +0 -25
- package/example/components/ui/label.tsx +0 -26
- package/example/components/ui/select.tsx +0 -164
- package/example/components/ui/separator.tsx +0 -31
- package/example/components/ui/sheet.tsx +0 -140
- package/example/components/ui/sonner.tsx +0 -31
- package/example/components/ui/switch.tsx +0 -29
- package/example/components/ui/textarea.tsx +0 -24
- package/example/components/ui/tooltip.tsx +0 -30
- package/example/components/user-menu.tsx +0 -53
- package/example/components.json +0 -17
- package/example/instrumentation.ts +0 -11
- package/example/lib/chat/guardrails.tsx +0 -181
- package/example/lib/chat/langchain-rag.tsx +0 -191
- package/example/lib/chat/langchain.tsx +0 -112
- package/example/lib/chat/late-update.tsx +0 -208
- package/example/lib/chat/manual.tsx +0 -605
- package/example/lib/chat/vercel-ai.tsx +0 -576
- package/example/lib/hooks/use-copy-to-clipboard.tsx +0 -33
- package/example/lib/hooks/use-enter-submit.tsx +0 -23
- package/example/lib/hooks/use-local-storage.ts +0 -24
- package/example/lib/hooks/use-scroll-anchor.tsx +0 -86
- package/example/lib/hooks/use-sidebar.tsx +0 -60
- package/example/lib/hooks/use-streamable-text.ts +0 -25
- package/example/lib/types.ts +0 -41
- package/example/lib/utils.ts +0 -89
- package/example/middleware.ts +0 -8
- package/example/next-env.d.ts +0 -5
- package/example/next.config.js +0 -16
- package/example/package-lock.json +0 -10917
- package/example/package.json +0 -84
- package/example/pnpm-lock.yaml +0 -5712
- package/example/postcss.config.js +0 -6
- package/example/prettier.config.cjs +0 -34
- package/example/public/apple-touch-icon.png +0 -0
- package/example/public/favicon-16x16.png +0 -0
- package/example/public/favicon.ico +0 -0
- package/example/public/next.svg +0 -1
- package/example/public/thirteen.svg +0 -1
- package/example/public/vercel.svg +0 -1
- package/example/tailwind.config.ts +0 -81
- package/example/tsconfig.json +0 -35
- package/src/LangWatchExporter.ts +0 -96
- package/src/evaluations.ts +0 -219
- package/src/index.test.ts +0 -402
- package/src/langchain.ts +0 -557
- package/src/typeUtils.ts +0 -89
- package/src/types.ts +0 -82
- package/src/utils.ts +0 -205
- /package/src/{server/types → internal/generated/openapi}/.gitkeep +0 -0
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import * as React from 'react'
|
|
4
|
-
|
|
5
|
-
import { useSidebar } from '@/lib/hooks/use-sidebar'
|
|
6
|
-
import { Button } from '@/components/ui/button'
|
|
7
|
-
import { IconSidebar } from '@/components/ui/icons'
|
|
8
|
-
|
|
9
|
-
export function SidebarToggle() {
|
|
10
|
-
const { toggleSidebar } = useSidebar()
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<Button
|
|
14
|
-
variant="ghost"
|
|
15
|
-
className="-ml-2 hidden size-9 p-0 lg:flex"
|
|
16
|
-
onClick={() => {
|
|
17
|
-
toggleSidebar()
|
|
18
|
-
}}
|
|
19
|
-
>
|
|
20
|
-
<IconSidebar className="size-6" />
|
|
21
|
-
<span className="sr-only">Toggle Sidebar</span>
|
|
22
|
-
</Button>
|
|
23
|
-
)
|
|
24
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import * as React from 'react'
|
|
4
|
-
|
|
5
|
-
import { useSidebar } from '@/lib/hooks/use-sidebar'
|
|
6
|
-
import { cn } from '@/lib/utils'
|
|
7
|
-
|
|
8
|
-
export interface SidebarProps extends React.ComponentProps<'div'> {}
|
|
9
|
-
|
|
10
|
-
export function Sidebar({ className, children }: SidebarProps) {
|
|
11
|
-
const { isSidebarOpen, isLoading } = useSidebar()
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<div
|
|
15
|
-
data-state={isSidebarOpen && !isLoading ? 'open' : 'closed'}
|
|
16
|
-
className={cn(className, 'h-full flex-col dark:bg-zinc-950')}
|
|
17
|
-
>
|
|
18
|
-
{children}
|
|
19
|
-
</div>
|
|
20
|
-
)
|
|
21
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useFormState, useFormStatus } from 'react-dom'
|
|
4
|
-
import { signup } from '@/app/signup/actions'
|
|
5
|
-
import Link from 'next/link'
|
|
6
|
-
import { useEffect } from 'react'
|
|
7
|
-
import { toast } from 'sonner'
|
|
8
|
-
import { IconSpinner } from './ui/icons'
|
|
9
|
-
import { getMessageFromCode } from '@/lib/utils'
|
|
10
|
-
import { useRouter } from 'next/navigation'
|
|
11
|
-
|
|
12
|
-
export default function SignupForm() {
|
|
13
|
-
const router = useRouter()
|
|
14
|
-
const [result, dispatch] = useFormState(signup, undefined)
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
if (result) {
|
|
18
|
-
if (result.type === 'error') {
|
|
19
|
-
toast.error(getMessageFromCode(result.resultCode))
|
|
20
|
-
} else {
|
|
21
|
-
toast.success(getMessageFromCode(result.resultCode))
|
|
22
|
-
router.refresh()
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}, [result, router])
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<form
|
|
29
|
-
action={dispatch}
|
|
30
|
-
className="flex flex-col items-center gap-4 space-y-3"
|
|
31
|
-
>
|
|
32
|
-
<div className="w-full flex-1 rounded-lg border bg-white px-6 pb-4 pt-8 shadow-md md:w-96 dark:bg-zinc-950">
|
|
33
|
-
<h1 className="mb-3 text-2xl font-bold">Sign up for an account!</h1>
|
|
34
|
-
<div className="w-full">
|
|
35
|
-
<div>
|
|
36
|
-
<label
|
|
37
|
-
className="mb-3 mt-5 block text-xs font-medium text-zinc-400"
|
|
38
|
-
htmlFor="email"
|
|
39
|
-
>
|
|
40
|
-
Email
|
|
41
|
-
</label>
|
|
42
|
-
<div className="relative">
|
|
43
|
-
<input
|
|
44
|
-
className="peer block w-full rounded-md border bg-zinc-50 px-2 py-[9px] text-sm outline-none placeholder:text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950"
|
|
45
|
-
id="email"
|
|
46
|
-
type="email"
|
|
47
|
-
name="email"
|
|
48
|
-
placeholder="Enter your email address"
|
|
49
|
-
required
|
|
50
|
-
/>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
<div className="mt-4">
|
|
54
|
-
<label
|
|
55
|
-
className="mb-3 mt-5 block text-xs font-medium text-zinc-400"
|
|
56
|
-
htmlFor="password"
|
|
57
|
-
>
|
|
58
|
-
Password
|
|
59
|
-
</label>
|
|
60
|
-
<div className="relative">
|
|
61
|
-
<input
|
|
62
|
-
className="peer block w-full rounded-md border bg-zinc-50 px-2 py-[9px] text-sm outline-none placeholder:text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950"
|
|
63
|
-
id="password"
|
|
64
|
-
type="password"
|
|
65
|
-
name="password"
|
|
66
|
-
placeholder="Enter password"
|
|
67
|
-
required
|
|
68
|
-
minLength={6}
|
|
69
|
-
/>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
<LoginButton />
|
|
74
|
-
</div>
|
|
75
|
-
|
|
76
|
-
<Link href="/login" className="flex flex-row gap-1 text-sm text-zinc-400">
|
|
77
|
-
Already have an account?
|
|
78
|
-
<div className="font-semibold underline">Log in</div>
|
|
79
|
-
</Link>
|
|
80
|
-
</form>
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function LoginButton() {
|
|
85
|
-
const { pending } = useFormStatus()
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<button
|
|
89
|
-
className="my-4 flex h-10 w-full flex-row items-center justify-center rounded-md bg-zinc-900 p-2 text-sm font-semibold text-zinc-100 hover:bg-zinc-800 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-200"
|
|
90
|
-
aria-disabled={pending}
|
|
91
|
-
>
|
|
92
|
-
{pending ? <IconSpinner /> : 'Create account'}
|
|
93
|
-
</button>
|
|
94
|
-
)
|
|
95
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const placeholderEvents = [
|
|
2
|
-
{
|
|
3
|
-
date: '2022-10-01',
|
|
4
|
-
headline: 'NVIDIA releases new AI-powered graphics card',
|
|
5
|
-
description:
|
|
6
|
-
'NVIDIA unveils the latest graphics card infused with AI capabilities, revolutionizing gaming and rendering experiences.'
|
|
7
|
-
}
|
|
8
|
-
]
|
|
9
|
-
|
|
10
|
-
export const EventsSkeleton = () => {
|
|
11
|
-
return (
|
|
12
|
-
<div className="-mt-2 flex w-full flex-col gap-2 py-4">
|
|
13
|
-
{placeholderEvents.map(event => (
|
|
14
|
-
<div
|
|
15
|
-
key={event.date}
|
|
16
|
-
className="flex shrink-0 flex-col gap-1 rounded-lg bg-zinc-800 p-4"
|
|
17
|
-
>
|
|
18
|
-
<div className="w-fit rounded-md bg-zinc-700 text-sm text-transparent">
|
|
19
|
-
{event.date}
|
|
20
|
-
</div>
|
|
21
|
-
<div className="w-fit rounded-md bg-zinc-700 text-transparent">
|
|
22
|
-
{event.headline}
|
|
23
|
-
</div>
|
|
24
|
-
<div className="w-auto rounded-md bg-zinc-700 text-transparent">
|
|
25
|
-
{event.description.slice(0, 70)}...
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
))}
|
|
29
|
-
</div>
|
|
30
|
-
)
|
|
31
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { format, parseISO } from 'date-fns'
|
|
2
|
-
|
|
3
|
-
interface Event {
|
|
4
|
-
date: string
|
|
5
|
-
headline: string
|
|
6
|
-
description: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function Events({ props: events }: { props: Event[] }) {
|
|
10
|
-
return (
|
|
11
|
-
<div className="-mt-2 flex w-full flex-col gap-2 py-4">
|
|
12
|
-
{events.map(event => (
|
|
13
|
-
<div
|
|
14
|
-
key={event.date}
|
|
15
|
-
className="flex shrink-0 flex-col gap-1 rounded-lg bg-zinc-800 p-4"
|
|
16
|
-
>
|
|
17
|
-
<div className="text-sm text-zinc-400">
|
|
18
|
-
{format(parseISO(event.date), 'dd LLL, yyyy')}
|
|
19
|
-
</div>
|
|
20
|
-
<div className="text-base font-bold text-zinc-200">
|
|
21
|
-
{event.headline}
|
|
22
|
-
</div>
|
|
23
|
-
<div className="text-zinc-500">
|
|
24
|
-
{event.description.slice(0, 70)}...
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
))}
|
|
28
|
-
</div>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import dynamic from 'next/dynamic'
|
|
4
|
-
import { StockSkeleton } from './stock-skeleton'
|
|
5
|
-
import { StocksSkeleton } from './stocks-skeleton'
|
|
6
|
-
import { EventsSkeleton } from './events-skeleton'
|
|
7
|
-
|
|
8
|
-
export { spinner } from './spinner'
|
|
9
|
-
export { BotCard, BotMessage, SystemMessage } from './message'
|
|
10
|
-
|
|
11
|
-
const Stock = dynamic(() => import('./stock').then(mod => mod.Stock), {
|
|
12
|
-
ssr: false,
|
|
13
|
-
loading: () => <StockSkeleton />
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
const Purchase = dynamic(
|
|
17
|
-
() => import('./stock-purchase').then(mod => mod.Purchase),
|
|
18
|
-
{
|
|
19
|
-
ssr: false,
|
|
20
|
-
loading: () => (
|
|
21
|
-
<div className="h-[375px] rounded-xl border bg-zinc-950 p-4 text-green-400 sm:h-[314px]" />
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
const Stocks = dynamic(() => import('./stocks').then(mod => mod.Stocks), {
|
|
27
|
-
ssr: false,
|
|
28
|
-
loading: () => <StocksSkeleton />
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const Events = dynamic(() => import('./events').then(mod => mod.Events), {
|
|
32
|
-
ssr: false,
|
|
33
|
-
loading: () => <EventsSkeleton />
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
export { Stock, Purchase, Stocks, Events }
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { IconOpenAI, IconUser } from '@/components/ui/icons'
|
|
4
|
-
import { cn } from '@/lib/utils'
|
|
5
|
-
import { spinner } from './spinner'
|
|
6
|
-
import { CodeBlock } from '../ui/codeblock'
|
|
7
|
-
import { MemoizedReactMarkdown } from '../markdown'
|
|
8
|
-
import remarkGfm from 'remark-gfm'
|
|
9
|
-
import remarkMath from 'remark-math'
|
|
10
|
-
import { StreamableValue, useStreamableValue } from 'ai/rsc'
|
|
11
|
-
import { useStreamableText } from '@/lib/hooks/use-streamable-text'
|
|
12
|
-
|
|
13
|
-
// Different types of message bubbles.
|
|
14
|
-
|
|
15
|
-
export function UserMessage({ children }: { children: React.ReactNode }) {
|
|
16
|
-
return (
|
|
17
|
-
<div className="group relative flex items-start md:-ml-12">
|
|
18
|
-
<div className="flex size-[25px] shrink-0 select-none items-center justify-center rounded-md border bg-background shadow-sm">
|
|
19
|
-
<IconUser />
|
|
20
|
-
</div>
|
|
21
|
-
<div className="ml-4 flex-1 space-y-2 overflow-hidden pl-2">
|
|
22
|
-
{children}
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function BotMessage({
|
|
29
|
-
content,
|
|
30
|
-
className
|
|
31
|
-
}: {
|
|
32
|
-
content: string | StreamableValue<string>
|
|
33
|
-
className?: string
|
|
34
|
-
}) {
|
|
35
|
-
const text = useStreamableText(content)
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<div className={cn('group relative flex items-start md:-ml-12', className)}>
|
|
39
|
-
<div className="flex size-[24px] shrink-0 select-none items-center justify-center rounded-md border bg-primary text-primary-foreground shadow-sm">
|
|
40
|
-
<IconOpenAI />
|
|
41
|
-
</div>
|
|
42
|
-
<div className="ml-4 flex-1 space-y-2 overflow-hidden px-1">
|
|
43
|
-
<MemoizedReactMarkdown
|
|
44
|
-
className="prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0"
|
|
45
|
-
remarkPlugins={[remarkGfm, remarkMath]}
|
|
46
|
-
components={{
|
|
47
|
-
p({ children }) {
|
|
48
|
-
return <p className="mb-2 last:mb-0">{children}</p>
|
|
49
|
-
},
|
|
50
|
-
code({ node, inline, className, children, ...props }) {
|
|
51
|
-
if (children.length) {
|
|
52
|
-
if (children[0] == '▍') {
|
|
53
|
-
return (
|
|
54
|
-
<span className="mt-1 animate-pulse cursor-default">▍</span>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
children[0] = (children[0] as string).replace('`▍`', '▍')
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const match = /language-(\w+)/.exec(className || '')
|
|
62
|
-
|
|
63
|
-
if (inline) {
|
|
64
|
-
return (
|
|
65
|
-
<code className={className} {...props}>
|
|
66
|
-
{children}
|
|
67
|
-
</code>
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<CodeBlock
|
|
73
|
-
key={Math.random()}
|
|
74
|
-
language={(match && match[1]) || ''}
|
|
75
|
-
value={String(children).replace(/\n$/, '')}
|
|
76
|
-
{...props}
|
|
77
|
-
/>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
}}
|
|
81
|
-
>
|
|
82
|
-
{text}
|
|
83
|
-
</MemoizedReactMarkdown>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function BotCard({
|
|
90
|
-
children,
|
|
91
|
-
showAvatar = true
|
|
92
|
-
}: {
|
|
93
|
-
children: React.ReactNode
|
|
94
|
-
showAvatar?: boolean
|
|
95
|
-
}) {
|
|
96
|
-
return (
|
|
97
|
-
<div className="group relative flex items-start md:-ml-12">
|
|
98
|
-
<div
|
|
99
|
-
className={cn(
|
|
100
|
-
'flex size-[24px] shrink-0 select-none items-center justify-center rounded-md border bg-primary text-primary-foreground shadow-sm',
|
|
101
|
-
!showAvatar && 'invisible'
|
|
102
|
-
)}
|
|
103
|
-
>
|
|
104
|
-
<IconOpenAI />
|
|
105
|
-
</div>
|
|
106
|
-
<div className="ml-4 flex-1 pl-2">{children}</div>
|
|
107
|
-
</div>
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function SystemMessage({ children }: { children: React.ReactNode }) {
|
|
112
|
-
return (
|
|
113
|
-
<div
|
|
114
|
-
className={
|
|
115
|
-
'mt-2 flex items-center justify-center gap-2 text-xs text-gray-500'
|
|
116
|
-
}
|
|
117
|
-
>
|
|
118
|
-
<div className={'max-w-[600px] flex-initial p-2'}>{children}</div>
|
|
119
|
-
</div>
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function SpinnerMessage() {
|
|
124
|
-
return (
|
|
125
|
-
<div className="group relative flex items-start md:-ml-12">
|
|
126
|
-
<div className="flex size-[24px] shrink-0 select-none items-center justify-center rounded-md border bg-primary text-primary-foreground shadow-sm">
|
|
127
|
-
<IconOpenAI />
|
|
128
|
-
</div>
|
|
129
|
-
<div className="ml-4 h-[24px] flex flex-row items-center flex-1 space-y-2 overflow-hidden px-1">
|
|
130
|
-
{spinner}
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
)
|
|
134
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
export const spinner = (
|
|
4
|
-
<svg
|
|
5
|
-
fill="none"
|
|
6
|
-
stroke="currentColor"
|
|
7
|
-
strokeWidth="1.5"
|
|
8
|
-
viewBox="0 0 24 24"
|
|
9
|
-
strokeLinecap="round"
|
|
10
|
-
strokeLinejoin="round"
|
|
11
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
-
className="size-5 animate-spin stroke-zinc-400"
|
|
13
|
-
>
|
|
14
|
-
<path d="M12 3v3m6.366-.366-2.12 2.12M21 12h-3m.366 6.366-2.12-2.12M12 21v-3m-6.366.366 2.12-2.12M3 12h3m-.366-6.366 2.12 2.12"></path>
|
|
15
|
-
</svg>
|
|
16
|
-
)
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useId, useState } from 'react'
|
|
4
|
-
import { useActions, useAIState, useUIState } from 'ai/rsc'
|
|
5
|
-
import { formatNumber } from '@/lib/utils'
|
|
6
|
-
|
|
7
|
-
import type { AI } from '@/lib/chat/vercel-ai'
|
|
8
|
-
|
|
9
|
-
interface Purchase {
|
|
10
|
-
numberOfShares?: number
|
|
11
|
-
symbol: string
|
|
12
|
-
price: number
|
|
13
|
-
status: 'requires_action' | 'completed' | 'expired'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function Purchase({
|
|
17
|
-
props: { numberOfShares, symbol, price, status = 'expired' }
|
|
18
|
-
}: {
|
|
19
|
-
props: Purchase
|
|
20
|
-
}) {
|
|
21
|
-
const [value, setValue] = useState(numberOfShares || 100)
|
|
22
|
-
const [purchasingUI, setPurchasingUI] = useState<null | React.ReactNode>(null)
|
|
23
|
-
const [aiState, setAIState] = useAIState<typeof AI>()
|
|
24
|
-
const [, setMessages] = useUIState<typeof AI>()
|
|
25
|
-
const { confirmPurchase } = useActions()
|
|
26
|
-
|
|
27
|
-
// Unique identifier for this UI component.
|
|
28
|
-
const id = useId()
|
|
29
|
-
|
|
30
|
-
// Whenever the slider changes, we need to update the local value state and the history
|
|
31
|
-
// so LLM also knows what's going on.
|
|
32
|
-
function onSliderChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
33
|
-
const newValue = Number(e.target.value)
|
|
34
|
-
setValue(newValue)
|
|
35
|
-
|
|
36
|
-
// Insert a hidden history info to the list.
|
|
37
|
-
const message = {
|
|
38
|
-
role: 'system' as const,
|
|
39
|
-
content: `[User has changed to purchase ${newValue} shares of ${name}. Total cost: $${(
|
|
40
|
-
newValue * price
|
|
41
|
-
).toFixed(2)}]`,
|
|
42
|
-
|
|
43
|
-
// Identifier of this UI component, so we don't insert it many times.
|
|
44
|
-
id
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// If last history state is already this info, update it. This is to avoid
|
|
48
|
-
// adding every slider change to the history.
|
|
49
|
-
if (aiState.messages[aiState.messages.length - 1]?.id === id) {
|
|
50
|
-
setAIState({
|
|
51
|
-
...aiState,
|
|
52
|
-
messages: [...aiState.messages.slice(0, -1), message]
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// If it doesn't exist, append it to history.
|
|
59
|
-
setAIState({ ...aiState, messages: [...aiState.messages, message] })
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<div className="p-4 text-green-400 border rounded-xl bg-zinc-950">
|
|
64
|
-
<div className="inline-block float-right px-2 py-1 text-xs rounded-full bg-white/10">
|
|
65
|
-
+1.23% ↑
|
|
66
|
-
</div>
|
|
67
|
-
<div className="text-lg text-zinc-300">{symbol}</div>
|
|
68
|
-
<div className="text-3xl font-bold">${price}</div>
|
|
69
|
-
{purchasingUI ? (
|
|
70
|
-
<div className="mt-4 text-zinc-200">{purchasingUI}</div>
|
|
71
|
-
) : status === 'requires_action' ? (
|
|
72
|
-
<>
|
|
73
|
-
<div className="relative pb-6 mt-6">
|
|
74
|
-
<p>Shares to purchase</p>
|
|
75
|
-
<input
|
|
76
|
-
id="labels-range-input"
|
|
77
|
-
type="range"
|
|
78
|
-
value={value}
|
|
79
|
-
onChange={onSliderChange}
|
|
80
|
-
min="10"
|
|
81
|
-
max="1000"
|
|
82
|
-
className="w-full h-1 rounded-lg appearance-none cursor-pointer bg-zinc-600 accent-green-500 dark:bg-zinc-700"
|
|
83
|
-
/>
|
|
84
|
-
<span className="absolute text-xs bottom-1 start-0 text-zinc-400">
|
|
85
|
-
10
|
|
86
|
-
</span>
|
|
87
|
-
<span className="absolute text-xs -translate-x-1/2 bottom-1 start-1/3 text-zinc-400 rtl:translate-x-1/2">
|
|
88
|
-
100
|
|
89
|
-
</span>
|
|
90
|
-
<span className="absolute text-xs -translate-x-1/2 bottom-1 start-2/3 text-zinc-400 rtl:translate-x-1/2">
|
|
91
|
-
500
|
|
92
|
-
</span>
|
|
93
|
-
<span className="absolute text-xs bottom-1 end-0 text-zinc-400">
|
|
94
|
-
1000
|
|
95
|
-
</span>
|
|
96
|
-
</div>
|
|
97
|
-
|
|
98
|
-
<div className="mt-6">
|
|
99
|
-
<p>Total cost</p>
|
|
100
|
-
<div className="flex flex-wrap items-center text-xl font-bold sm:items-end sm:gap-2 sm:text-3xl">
|
|
101
|
-
<div className="flex flex-col basis-1/3 tabular-nums sm:basis-auto sm:flex-row sm:items-center sm:gap-2">
|
|
102
|
-
{value}
|
|
103
|
-
<span className="mb-1 text-sm font-normal text-zinc-600 sm:mb-0 dark:text-zinc-400">
|
|
104
|
-
shares
|
|
105
|
-
</span>
|
|
106
|
-
</div>
|
|
107
|
-
<div className="text-center basis-1/3 sm:basis-auto">×</div>
|
|
108
|
-
<span className="flex flex-col basis-1/3 tabular-nums sm:basis-auto sm:flex-row sm:items-center sm:gap-2">
|
|
109
|
-
${price}
|
|
110
|
-
<span className="mb-1 ml-1 text-sm font-normal text-zinc-600 sm:mb-0 dark:text-zinc-400">
|
|
111
|
-
per share
|
|
112
|
-
</span>
|
|
113
|
-
</span>
|
|
114
|
-
<div className="pt-2 mt-2 text-center border-t basis-full border-t-zinc-700 sm:mt-0 sm:basis-auto sm:border-0 sm:pt-0 sm:text-left">
|
|
115
|
-
= <span>{formatNumber(value * price)}</span>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
|
|
120
|
-
<button
|
|
121
|
-
className="w-full px-4 py-2 mt-6 font-bold bg-green-400 rounded-lg text-zinc-900 hover:bg-green-500"
|
|
122
|
-
onClick={async () => {
|
|
123
|
-
const response = await confirmPurchase(symbol, price, value)
|
|
124
|
-
setPurchasingUI(response.purchasingUI)
|
|
125
|
-
|
|
126
|
-
// Insert a new system message to the UI.
|
|
127
|
-
setMessages((currentMessages: any) => [
|
|
128
|
-
...currentMessages,
|
|
129
|
-
response.newMessage
|
|
130
|
-
])
|
|
131
|
-
}}
|
|
132
|
-
>
|
|
133
|
-
Purchase
|
|
134
|
-
</button>
|
|
135
|
-
</>
|
|
136
|
-
) : status === 'completed' ? (
|
|
137
|
-
<p className="mb-2 text-white">
|
|
138
|
-
You have successfully purchased {value} ${symbol}. Total cost:{' '}
|
|
139
|
-
{formatNumber(value * price)}
|
|
140
|
-
</p>
|
|
141
|
-
) : status === 'expired' ? (
|
|
142
|
-
<p className="mb-2 text-white">Your checkout session has expired!</p>
|
|
143
|
-
) : null}
|
|
144
|
-
</div>
|
|
145
|
-
)
|
|
146
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export const StockSkeleton = () => {
|
|
2
|
-
return (
|
|
3
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-950 p-4 text-green-400">
|
|
4
|
-
<div className="float-right inline-block w-fit rounded-full bg-zinc-700 px-2 py-1 text-xs text-transparent">
|
|
5
|
-
xxxxxxx
|
|
6
|
-
</div>
|
|
7
|
-
<div className="mb-1 w-fit rounded-md bg-zinc-700 text-lg text-transparent">
|
|
8
|
-
xxxx
|
|
9
|
-
</div>
|
|
10
|
-
<div className="w-fit rounded-md bg-zinc-700 text-3xl font-bold text-transparent">
|
|
11
|
-
xxxx
|
|
12
|
-
</div>
|
|
13
|
-
<div className="text mt-1 w-fit rounded-md bg-zinc-700 text-xs text-transparent">
|
|
14
|
-
xxxxxx xxx xx xxxx xx xxx
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
<div className="relative -mx-4 cursor-col-resize">
|
|
18
|
-
<div style={{ height: 146 }}></div>
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
)
|
|
22
|
-
}
|