langwatch 0.1.7 → 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.
Files changed (235) hide show
  1. package/.editorconfig +16 -0
  2. package/LICENSE +7 -0
  3. package/README.md +268 -1
  4. package/copy-types.sh +19 -8
  5. package/examples/langchain/.env.example +2 -0
  6. package/examples/langchain/README.md +42 -0
  7. package/examples/langchain/package-lock.json +2930 -0
  8. package/examples/langchain/package.json +27 -0
  9. package/examples/langchain/src/cli-markdown.d.ts +137 -0
  10. package/examples/langchain/src/index.ts +109 -0
  11. package/examples/langchain/tsconfig.json +25 -0
  12. package/examples/langgraph/.env.example +2 -0
  13. package/examples/langgraph/README.md +42 -0
  14. package/examples/langgraph/package-lock.json +3031 -0
  15. package/examples/langgraph/package.json +28 -0
  16. package/examples/langgraph/src/cli-markdown.d.ts +137 -0
  17. package/examples/langgraph/src/index.ts +196 -0
  18. package/examples/langgraph/tsconfig.json +25 -0
  19. package/examples/mastra/.env.example +2 -0
  20. package/examples/mastra/README.md +57 -0
  21. package/examples/mastra/package-lock.json +5296 -0
  22. package/examples/mastra/package.json +32 -0
  23. package/examples/mastra/src/cli-markdown.d.ts +137 -0
  24. package/examples/mastra/src/index.ts +120 -0
  25. package/examples/mastra/src/mastra/agents/weather-agent.ts +30 -0
  26. package/examples/mastra/src/mastra/index.ts +21 -0
  27. package/examples/mastra/src/mastra/tools/weather-tool.ts +102 -0
  28. package/examples/mastra/tsconfig.json +25 -0
  29. package/examples/vercel-ai/.env.example +2 -0
  30. package/examples/vercel-ai/README.md +38 -0
  31. package/examples/vercel-ai/package-lock.json +2571 -0
  32. package/examples/vercel-ai/package.json +27 -0
  33. package/examples/vercel-ai/src/cli-markdown.d.ts +137 -0
  34. package/examples/vercel-ai/src/index.ts +110 -0
  35. package/examples/vercel-ai/src/instrumentation.ts +9 -0
  36. package/examples/vercel-ai/tsconfig.json +25 -0
  37. package/package.json +80 -33
  38. package/src/__tests__/client-browser.test.ts +92 -0
  39. package/src/__tests__/client-node.test.ts +76 -0
  40. package/src/__tests__/client.test.ts +71 -0
  41. package/src/__tests__/integration/client-browser.test.ts +46 -0
  42. package/src/__tests__/integration/client-node.test.ts +46 -0
  43. package/src/client-browser.ts +70 -0
  44. package/src/client-node.ts +82 -0
  45. package/src/client-shared.ts +72 -0
  46. package/src/client.ts +119 -0
  47. package/src/evaluation/__tests__/record-evaluation.test.ts +112 -0
  48. package/src/evaluation/__tests__/run-evaluation.test.ts +171 -0
  49. package/src/evaluation/index.ts +2 -0
  50. package/src/evaluation/record-evaluation.ts +101 -0
  51. package/src/evaluation/run-evaluation.ts +133 -0
  52. package/src/evaluation/tracer.ts +3 -0
  53. package/src/evaluation/types.ts +23 -0
  54. package/src/index.ts +10 -591
  55. package/src/internal/api/__tests__/errors.test.ts +98 -0
  56. package/src/internal/api/client.ts +30 -0
  57. package/src/internal/api/errors.ts +32 -0
  58. package/src/internal/generated/types/.gitkeep +0 -0
  59. package/src/observability/__tests__/integration/base.test.ts +74 -0
  60. package/src/observability/__tests__/integration/browser-setup-ordering.test.ts +60 -0
  61. package/src/observability/__tests__/integration/complex-nested-spans.test.ts +29 -0
  62. package/src/observability/__tests__/integration/error-handling.test.ts +24 -0
  63. package/src/observability/__tests__/integration/langwatch-disabled-otel.test.ts +24 -0
  64. package/src/observability/__tests__/integration/langwatch-first-then-vercel.test.ts +24 -0
  65. package/src/observability/__tests__/integration/multiple-setup-attempts.test.ts +27 -0
  66. package/src/observability/__tests__/integration/otel-ordering.test.ts +27 -0
  67. package/src/observability/__tests__/integration/vercel-configurations.test.ts +20 -0
  68. package/src/observability/__tests__/integration/vercel-first-then-langwatch.test.ts +27 -0
  69. package/src/observability/__tests__/span.test.ts +214 -0
  70. package/src/observability/__tests__/trace.test.ts +180 -0
  71. package/src/observability/exporters/index.ts +1 -0
  72. package/src/observability/exporters/langwatch-exporter.ts +53 -0
  73. package/src/observability/index.ts +4 -0
  74. package/src/observability/instrumentation/langchain/__tests__/integration/langchain-chatbot.test.ts +112 -0
  75. package/src/observability/instrumentation/langchain/__tests__/langchain.test.ts +284 -0
  76. package/src/observability/instrumentation/langchain/index.ts +624 -0
  77. package/src/observability/processors/__tests__/filterable-batch-span-exporter.test.ts +98 -0
  78. package/src/observability/processors/filterable-batch-span-processor.ts +99 -0
  79. package/src/observability/processors/index.ts +1 -0
  80. package/src/observability/semconv/attributes.ts +185 -0
  81. package/src/observability/semconv/events.ts +42 -0
  82. package/src/observability/semconv/index.ts +16 -0
  83. package/src/observability/semconv/values.ts +159 -0
  84. package/src/observability/span.ts +728 -0
  85. package/src/observability/trace.ts +301 -0
  86. package/src/prompt/__tests__/prompt.test.ts +139 -0
  87. package/src/prompt/get-prompt-version.ts +49 -0
  88. package/src/prompt/get-prompt.ts +44 -0
  89. package/src/prompt/index.ts +3 -0
  90. package/src/prompt/prompt.ts +133 -0
  91. package/src/prompt/service.ts +221 -0
  92. package/src/prompt/tracer.ts +3 -0
  93. package/src/prompt/types.ts +0 -0
  94. package/ts-to-zod.config.js +11 -0
  95. package/tsconfig.json +3 -9
  96. package/tsup.config.ts +11 -1
  97. package/vitest.config.ts +1 -0
  98. package/dist/chunk-FWBCQQYZ.mjs +0 -711
  99. package/dist/chunk-FWBCQQYZ.mjs.map +0 -1
  100. package/dist/index.d.mts +0 -1010
  101. package/dist/index.d.ts +0 -1010
  102. package/dist/index.js +0 -27294
  103. package/dist/index.js.map +0 -1
  104. package/dist/index.mjs +0 -959
  105. package/dist/index.mjs.map +0 -1
  106. package/dist/utils-B0pgWcps.d.mts +0 -303
  107. package/dist/utils-B0pgWcps.d.ts +0 -303
  108. package/dist/utils.d.mts +0 -2
  109. package/dist/utils.d.ts +0 -2
  110. package/dist/utils.js +0 -703
  111. package/dist/utils.js.map +0 -1
  112. package/dist/utils.mjs +0 -11
  113. package/dist/utils.mjs.map +0 -1
  114. package/example/.env.example +0 -12
  115. package/example/.eslintrc.json +0 -26
  116. package/example/LICENSE +0 -13
  117. package/example/README.md +0 -12
  118. package/example/app/(chat)/chat/[id]/page.tsx +0 -60
  119. package/example/app/(chat)/layout.tsx +0 -14
  120. package/example/app/(chat)/page.tsx +0 -27
  121. package/example/app/actions.ts +0 -156
  122. package/example/app/globals.css +0 -76
  123. package/example/app/guardrails/page.tsx +0 -26
  124. package/example/app/langchain/page.tsx +0 -27
  125. package/example/app/langchain-rag/page.tsx +0 -28
  126. package/example/app/late-update/page.tsx +0 -27
  127. package/example/app/layout.tsx +0 -64
  128. package/example/app/login/actions.ts +0 -71
  129. package/example/app/login/page.tsx +0 -18
  130. package/example/app/manual/page.tsx +0 -27
  131. package/example/app/new/page.tsx +0 -5
  132. package/example/app/opengraph-image.png +0 -0
  133. package/example/app/share/[id]/page.tsx +0 -58
  134. package/example/app/signup/actions.ts +0 -111
  135. package/example/app/signup/page.tsx +0 -18
  136. package/example/app/twitter-image.png +0 -0
  137. package/example/auth.config.ts +0 -42
  138. package/example/auth.ts +0 -45
  139. package/example/components/button-scroll-to-bottom.tsx +0 -36
  140. package/example/components/chat-history.tsx +0 -49
  141. package/example/components/chat-list.tsx +0 -52
  142. package/example/components/chat-message-actions.tsx +0 -40
  143. package/example/components/chat-message.tsx +0 -80
  144. package/example/components/chat-panel.tsx +0 -139
  145. package/example/components/chat-share-dialog.tsx +0 -95
  146. package/example/components/chat.tsx +0 -84
  147. package/example/components/clear-history.tsx +0 -75
  148. package/example/components/empty-screen.tsx +0 -38
  149. package/example/components/external-link.tsx +0 -29
  150. package/example/components/footer.tsx +0 -19
  151. package/example/components/header.tsx +0 -114
  152. package/example/components/login-button.tsx +0 -42
  153. package/example/components/login-form.tsx +0 -97
  154. package/example/components/markdown.tsx +0 -9
  155. package/example/components/prompt-form.tsx +0 -115
  156. package/example/components/providers.tsx +0 -17
  157. package/example/components/sidebar-actions.tsx +0 -125
  158. package/example/components/sidebar-desktop.tsx +0 -19
  159. package/example/components/sidebar-footer.tsx +0 -16
  160. package/example/components/sidebar-item.tsx +0 -124
  161. package/example/components/sidebar-items.tsx +0 -42
  162. package/example/components/sidebar-list.tsx +0 -38
  163. package/example/components/sidebar-mobile.tsx +0 -31
  164. package/example/components/sidebar-toggle.tsx +0 -24
  165. package/example/components/sidebar.tsx +0 -21
  166. package/example/components/signup-form.tsx +0 -95
  167. package/example/components/stocks/events-skeleton.tsx +0 -31
  168. package/example/components/stocks/events.tsx +0 -30
  169. package/example/components/stocks/index.tsx +0 -36
  170. package/example/components/stocks/message.tsx +0 -134
  171. package/example/components/stocks/spinner.tsx +0 -16
  172. package/example/components/stocks/stock-purchase.tsx +0 -146
  173. package/example/components/stocks/stock-skeleton.tsx +0 -22
  174. package/example/components/stocks/stock.tsx +0 -210
  175. package/example/components/stocks/stocks-skeleton.tsx +0 -9
  176. package/example/components/stocks/stocks.tsx +0 -67
  177. package/example/components/tailwind-indicator.tsx +0 -14
  178. package/example/components/theme-toggle.tsx +0 -31
  179. package/example/components/ui/alert-dialog.tsx +0 -141
  180. package/example/components/ui/badge.tsx +0 -36
  181. package/example/components/ui/button.tsx +0 -57
  182. package/example/components/ui/codeblock.tsx +0 -148
  183. package/example/components/ui/dialog.tsx +0 -122
  184. package/example/components/ui/dropdown-menu.tsx +0 -205
  185. package/example/components/ui/icons.tsx +0 -507
  186. package/example/components/ui/input.tsx +0 -25
  187. package/example/components/ui/label.tsx +0 -26
  188. package/example/components/ui/select.tsx +0 -164
  189. package/example/components/ui/separator.tsx +0 -31
  190. package/example/components/ui/sheet.tsx +0 -140
  191. package/example/components/ui/sonner.tsx +0 -31
  192. package/example/components/ui/switch.tsx +0 -29
  193. package/example/components/ui/textarea.tsx +0 -24
  194. package/example/components/ui/tooltip.tsx +0 -30
  195. package/example/components/user-menu.tsx +0 -53
  196. package/example/components.json +0 -17
  197. package/example/instrumentation.ts +0 -11
  198. package/example/lib/chat/guardrails.tsx +0 -181
  199. package/example/lib/chat/langchain-rag.tsx +0 -191
  200. package/example/lib/chat/langchain.tsx +0 -112
  201. package/example/lib/chat/late-update.tsx +0 -208
  202. package/example/lib/chat/manual.tsx +0 -605
  203. package/example/lib/chat/vercel-ai.tsx +0 -576
  204. package/example/lib/hooks/use-copy-to-clipboard.tsx +0 -33
  205. package/example/lib/hooks/use-enter-submit.tsx +0 -23
  206. package/example/lib/hooks/use-local-storage.ts +0 -24
  207. package/example/lib/hooks/use-scroll-anchor.tsx +0 -86
  208. package/example/lib/hooks/use-sidebar.tsx +0 -60
  209. package/example/lib/hooks/use-streamable-text.ts +0 -25
  210. package/example/lib/types.ts +0 -41
  211. package/example/lib/utils.ts +0 -89
  212. package/example/middleware.ts +0 -8
  213. package/example/next-env.d.ts +0 -5
  214. package/example/next.config.js +0 -16
  215. package/example/package-lock.json +0 -9990
  216. package/example/package.json +0 -84
  217. package/example/pnpm-lock.yaml +0 -5712
  218. package/example/postcss.config.js +0 -6
  219. package/example/prettier.config.cjs +0 -34
  220. package/example/public/apple-touch-icon.png +0 -0
  221. package/example/public/favicon-16x16.png +0 -0
  222. package/example/public/favicon.ico +0 -0
  223. package/example/public/next.svg +0 -1
  224. package/example/public/thirteen.svg +0 -1
  225. package/example/public/vercel.svg +0 -1
  226. package/example/tailwind.config.ts +0 -81
  227. package/example/tsconfig.json +0 -35
  228. package/src/LangWatchExporter.ts +0 -91
  229. package/src/evaluations.ts +0 -219
  230. package/src/index.test.ts +0 -402
  231. package/src/langchain.ts +0 -557
  232. package/src/typeUtils.ts +0 -89
  233. package/src/types.ts +0 -79
  234. package/src/utils.ts +0 -205
  235. /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
- }